# SDKs

Costfluent veröffentlicht drei Clients für die Public API, quelloffen unter der Apache-2.0-Lizenz: [costfluent-go](https://github.com/costfluent/costfluent-go) für Go, [costfluent-python](https://github.com/costfluent/costfluent-python) für Python und [costfluent-typescript](https://github.com/costfluent/costfluent-typescript) für TypeScript. Die Clients für Python und TypeScript werden aus dem OpenAPI-Dokument der API erzeugt und decken jede Operation ab; der Go-Client ist von Hand geschrieben, und seine Tests halten seine typisierten Methoden an dasselbe Dokument. Alle drei senden dein API-Token als Bearer-Token, folgen den Seiten einer Liste, wiederholen, was sich gefahrlos wiederholen lässt, und geben Geldbeträge als exakte Dezimalzeichenketten weiter.

## Bevor du beginnst

<PlanAvailability feature="ApiTokens" plan="Pro" />

- Das Token zu erstellen erfordert die Organisationsrolle Eigentümer, Integrationsverantwortliche Person oder Bearbeiter; siehe [Authentifizierung bei der Public API](/de/api/authentication).
- Go ab 1.24, Python ab 3.11 oder Node.js ab 20. Der TypeScript-Client läuft überall, wo es `fetch` gibt, also auch in Deno, Bun und Edge-Runtimes.

## Token erstellen

<Stepper>

1. Öffne **Einstellungen**, dann **API-Token**, und wähle **API-Token erstellen**.
2. Gib einen **Name** ein, der sagt, welches Skript das Token nutzt, etwa `Nächtlicher Budget-Export`.
3. Wähle unter **Zugriff** den Arbeitsbereich, in dem das Skript arbeitet, damit das Token nichts anderes erreicht. Behalte **Gesamte Organisation** nur, wenn das Skript mehrere Arbeitsbereiche liest.
4. Wähle unter **Vorlage** die Vorlage **Nur lesen**, füge die Schreibberechtigungen hinzu, die das Skript braucht, und entferne jede Berechtigung, die es nicht aufruft. Die Beispiele auf dieser Seite brauchen **Arbeitsbereiche anzeigen** und, um einen Ordner anzulegen, **Ordner erstellen**.
5. Wähle den **Ablauf**, etwa 90 Tage, und wähle **API-Token erstellen**.
6. Kopiere das Token aus **Ihr API-Token**. Es wird nur einmal angezeigt.

</Stepper>

:::caution
Die Clients holen sich das Token nirgends selbst, dein Code übergibt es ihnen. Bewahre es in einer Umgebungsvariable oder einem Secret Store auf, nie im Quellcode, und lass den TypeScript-Client auf einem Server laufen: Ein Token in einem Browser-Bundle kann jeder lesen.
:::

## Client installieren

<CodeTabs>

```bash title="Go"
go get github.com/costfluent/costfluent-go
```

```bash title="Python"
pip install costfluent
```

```bash title="TypeScript"
npm install @costfluent/sdk
```

</CodeTabs>

Das Go-Paket importierst du als `github.com/costfluent/costfluent-go/costfluent`.

## Erster Aufruf

Jedes Beispiel liest das Token aus `COSTFLUENT_API_TOKEN` und listet alle Arbeitsbereiche, die das Token sieht.

<CodeTabs>

```go title="Go"
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/costfluent/costfluent-go/costfluent"
)

func main() {
	client := costfluent.NewClient(costfluent.WithAPIKey(os.Getenv("COSTFLUENT_API_TOKEN")))
	ctx := context.Background()

	workspaces, err := client.ListAllWorkspaces(ctx, 100)
	if err != nil {
		log.Fatal(err)
	}
	for _, workspace := range workspaces {
		fmt.Println(workspace.ID, workspace.Name)
	}
}
```

```python title="Python"
import os

from costfluent import create_client, paginate
from costfluent.api.workspaces import list_workspaces

client = create_client(os.environ["COSTFLUENT_API_TOKEN"])

for workspace in paginate(list_workspaces.sync, client=client, limit=100):
    print(workspace.id, workspace.name)
```

```typescript title="TypeScript"
import * as costfluent from "@costfluent/sdk";

const client = costfluent.createClient({ token: process.env.COSTFLUENT_API_TOKEN! });

for await (const workspace of costfluent.paginate((querySerializer) =>
  client.GET("/v1/workspaces", { params: { query: { limit: 100 } }, querySerializer }),
)) {
  console.log(workspace.id, workspace.name);
}
```

</CodeTabs>

In Python ist jede Operation ein Modul unter `costfluent.api` mit den Funktionen `sync` und `asyncio` und ihren `_detailed`-Varianten, die zusätzlich Status und Header liefern; die Modelle liegen unter `costfluent.models`. In TypeScript sprichst du eine Operation über Methode und Pfad an, und Pfad, Query und Body sind aus der API typisiert. In Go ist jede Operation, die der Client abdeckt, eine Methode des Clients.

## Alle Seiten lesen

Eine Liste antwortet seitenweise ([Paginierung der Public API](/de/api/pagination)). Jeder Client liest die Seiten für dich:

- **Go**: Eine paginierte Ressource hat eine `ListAll`-Methode, etwa `ListAllWorkspaces(ctx, pageSize)` oder `ListAllBudgets(ctx, workspaceID, pageSize)`, die Seite um Seite anfragt, bis `links.next` leer ist. Die einfache Listenmethode liest mit `PageOptions` eine Seite und liefert deren `Links`.
- **Python**: `paginate` nimmt eine erzeugte `sync`-Funktion mit ihren Argumenten und liefert jedes Element; `apaginate` macht dasselbe für eine `asyncio`-Funktion.
- **TypeScript**: `paginate` nimmt eine Funktion, die den typisierten Aufruf macht und den übergebenen `querySerializer` weiterreicht, und liefert jedes Element.

Python und TypeScript folgen `links.next` und fragen die nächste Seite immer bei der Basis-URL des Clients an, sodass das Token nirgendwo anders hingeht.

### Kostendaten aus einem Stand lesen

Landet eine Synchronisierung, während du Kostendaten seitenweise liest, kann eine spätere Seite eine Zeile wiederholen oder auslassen ([Eine Synchronisierung zwischen den Seiten bemerken](/de/api/pagination#eine-synchronisierung-zwischen-den-seiten-bemerken)). Lies die erste Seite und sende ihre `meta.dataVersion` mit allen weiteren: `DataVersion` in `CostDataQuery` in Go, `data_version` in Python, `dataVersion` in TypeScript. `paginate` in Python und TypeScript behält sie von Seite zu Seite bei, weil `links.next` sie mitträgt.

<CodeTabs>

```go title="Go"
query := &costfluent.CostDataQuery{
	CostFilterOptions: costfluent.CostFilterOptions{StartDate: "2026-09-01", EndDate: "2026-09-30"},
}
var rows []costfluent.CostDataPoint
for query.Page = 1; ; query.Page++ {
	page, err := client.QueryCostData(ctx, query)
	if err != nil {
		log.Fatal(err)
	}
	rows = append(rows, page.Data...)
	if !page.Links.HasNextPage() {
		break
	}
	query.DataVersion = page.Meta.DataVersion
}
```

```python title="Python"
import datetime

from costfluent import paginate
from costfluent.api.cost_data import get_cost_data

window = {"start_date": datetime.date(2026, 9, 1), "end_date": datetime.date(2026, 9, 30), "limit": 5000}

first = get_cost_data.sync(client=client, **window)
rows = [
    *first.data,
    *paginate(get_cost_data.sync, client=client, **window, page=2, data_version=first.meta.data_version),
]
```

```typescript title="TypeScript"
const query = { startDate: "2026-09-01", endDate: "2026-09-30", limit: 5000 };

const { data: first } = await client.GET("/v1/costs", { params: { query } });
const rows = [...first!.data];
for await (const row of costfluent.paginate((querySerializer) =>
  client.GET("/v1/costs", {
    params: { query: { ...query, page: 2, dataVersion: first!.meta.dataVersion } },
    querySerializer,
  }),
)) {
  rows.push(row);
}
```

</CodeTabs>

Eine Seite, deren Daten eine Synchronisierung seit der ersten geändert hat, scheitert mit dem `409` der API: `costfluent.IsConflict(err)` in Go, ein `CostfluentError` mit dem `code` `conflict` in Python und TypeScript. Lies den Zeitraum dann ab Seite 1 neu.

## Beträge bleiben exakt

Ein Geldbetrag kommt als die exakte Dezimalzeichenkette an, die die API schreibt, etwa `"1250.50000000"`, nie als binäre Gleitkommazahl ([Datenformate der Public API](/de/api/formats)).

- **Go**: Ein Betrag ist ein `costfluent.Decimal`. `Equal` und `Cmp` vergleichen zwei Beträge nach ihrem Wert, gleich mit wie vielen Nachkommastellen, `Rat` liefert den exakten Wert, und `costfluent.ParseDecimal` prüft einen Betrag, bevor du ihn sendest.
- **Python**: Ein Betrag ist ein `str`. Wandle ihn mit `decimal.Decimal(amount)` um, wenn du damit rechnest.
- **TypeScript**: Ein Betrag ist ein `string`. Lass ihn so, oder rechne mit einer Dezimalbibliothek.

## Wiederholungen

Alle drei Clients wiederholen Anfragen selbst, nach denselben Regeln:

- Ein `429` wird bei jeder Methode wiederholt, nach dem `Retry-After` oder `RateLimit-Reset`, den die API sendet ([Rate Limits der Public API](/de/api/rate-limits)).
- Ein `5xx`, ein Verbindungsfehler oder ein `404`, der nicht der Fehler-Body der API ist, sondern der eines Proxys, wird bei `GET`, `HEAD`, `PUT`, `DELETE` und beim Anlegen wiederholt. Andere `POST`-Anfragen, die Arbeit anstoßen wie eine Synchronisierung, werden nicht wiederholt, ebenso wenig eine Anfrage mit `If-Match`: Eine Anfrage erneut zu senden, die die API schon ausgeführt hat, könnte sie doppelt ausführen oder an ihrer eigenen Änderung scheitern.
- Die Wartezeit wächst exponentiell ab einer Sekunde, mit Zufallsanteil, höchstens dreimal. Mit `max_retries` in Python und `maxRetries` in TypeScript änderst du die Anzahl.
- Eine Wartezeit über 60 Sekunden bedeutet, dass das Anfragebudget des Tokens aufgebraucht ist: Der Aufruf scheitert sofort mit dem `429`, statt abzuwarten.
- Eine Anfrage, die in ein Timeout läuft, wird nicht wiederholt. Das Timeout beträgt 30 Sekunden, sofern du kein anderes setzt: `WithTimeout` in Go, `timeout` in Python, `timeoutMs` in TypeScript.

## Anlegen ohne Duplikate

Jedes Anlegen sendet einen neuen `Idempotency-Key` und bei jeder Wiederholung denselben, sodass die API ein wiederholtes Anlegen mit ihrer ersten Antwort beantwortet, statt ein zweites Objekt anzulegen ([Datenformate der Public API](/de/api/formats)). In Python und TypeScript kannst du den Schlüssel selbst wählen, etwa damit ein Job, der kurz nach einem Fehler noch einmal läuft, den Ordner des ersten Laufs zurückbekommt; der Go-Client erzeugt seinen Schlüssel immer selbst.

<CodeTabs>

```go title="Go"
folder, err := client.CreateFolder(ctx, &costfluent.CreateFolderInput{
	WorkspaceID: "wsp_...",
	Title:       "Finanzen",
})
```

```python title="Python"
from costfluent import models
from costfluent.api.folders import create_folder

folder = create_folder.sync(
    client=client,
    body=models.CreateFolderRequest(workspace_id="wsp_...", title="Finanzen"),
    idempotency_key="ordner-finanzen-einrichten",
)
```

```typescript title="TypeScript"
const { data: folder } = await client.POST("/v1/folders", {
  params: { header: { "Idempotency-Key": "ordner-finanzen-einrichten" } },
  body: { workspaceId: "wsp_...", title: "Finanzen" },
});
```

</CodeTabs>

Einen Schlüssel, den die API schon mit einer anderen Anfrage gesehen hat, lehnt sie mit `idempotency_key_reused` ab: `costfluent.IsIdempotencyKeyReused(err)` in Go, `is_idempotency_key_reused` in Python und `isIdempotencyKeyReused` in TypeScript.

## Versionen und API-Vertrag

Die drei Clients teilen sich eine Versionsnummer, `1.MINOR.PATCH`; Go-Module schreiben sie mit einem führenden `v`. Die Hauptversion ist die Pfadversion der API, `v1`. Die Nebenversion steigt mit jedem Release, das einen neueren API-Vertrag enthält, und die Patch-Version ist eine Korrektur nur am Client.

Jeder Client trägt außerdem den SHA-256 des Public-API-Dokuments, aus dem er gebaut wurde: `costfluent.ContractSHA256` in Go, `costfluent.CONTRACT_SHA256` in Python und `CONTRACT_SHA256`, exportiert von `@costfluent/sdk`, in TypeScript. Zwei Clients mit demselben Hash wurden aus demselben Vertrag gebaut. Was sich in Version 1 der API ändern darf und was nicht, steht unter [Versionierung der Public API](/de/api/versioning).

## Wie es sich verhält

- Lehnt die API einen Aufruf ab, scheitert er nach den Wiederholungen mit dem Fehler der API: einem `*costfluent.APIError` in Go, einem `CostfluentError` in Python und TypeScript, jeweils mit dem Status und dem `code` der API ([Fehler der Public API](/de/api/errors)).
- Der `404` eines Proxys, während die API neu startet, gilt nie als fehlendes Objekt: `costfluent.IsNotFound(err)` in Go, `is_not_found` in Python und `isNotFound` in TypeScript sind nur bei der eigenen Antwort der API wahr.
- In Go nimmt eine arbeitsbereichsbezogene Methode eine Arbeitsbereichs-ID oder greift auf den Standard aus `WithWorkspace` zurück; ein Aufruf ohne beides scheitert, bevor er gesendet wird. Ein Workspace-Token liest nur seinen eigenen Arbeitsbereich, und die API lehnt jeden anderen ab.
- Jeder Aufruf zählt gegen das Anfragelimit des Tokens.
- Die Clients rufen `https://api.costfluent.com` auf, sofern du keine andere Basis-URL setzt.

## Verwandte Seiten

- [Authentifizierung bei der Public API](/de/api/authentication)
- [Paginierung der Public API](/de/api/pagination)
- [Datenformate der Public API](/de/api/formats)
- [MCP-Server](/de/developers/mcp)
- [API-Referenz](/api)
