# Quickstart

> Pull your account's results and the token usage behind them, using nothing but HTTP requests.

Canonical page: https://www.rundiffusion.com/docs/api/quickstart
Authorization: OAuth device flow or Personal API Access Token or Company API Access Token

---

This walks through one useful end-to-end task: list what your account holds, then
pull the token usage that accounts for it. Those two answers together are what
most reporting and cost allocation work is built on.

You will need a RunDiffusion account. The reporting half additionally needs an
Enterprise plan; everything else works on any plan.

## 1. Get your credentials

Which one you need is decided by the endpoint you are calling and by who the
work belongs to.

| Credential | What it is |
| --- | --- |
| OAuth device flow | An ID token from the RFC 8628 device code flow. Works for personal and team calls alike, so it is the alternative to either token. |
| Personal API Access Token | For personal plans. A long-lived key that acts as you on your personal account: your library, boards, uploads, and generations. |
| Company API Access Token | For team and enterprise plans. A long-lived key that acts as you on your company's teams, plus company usage reporting. |

A **Public API Access Token** is a long-lived key from the **Plugins & APIs**
page. On a personal plan you create a Personal token yourself. On a team or
enterprise plan, an account administrator creates a Company token and associates
it to your user. Both act as you, so anything they do is attributed to you.

The **OAuth token** comes from the device flow and works for either. Prefer it
when a real person signs in to an app you distribute.
[Authentication](/docs/api/authentication) has the full walkthrough for both.

Put them in your environment so the rest of the steps can read them:

```bash
export RUNDIFFUSION_API_TOKEN="<your Public API Access Token>"
export RUNDIFFUSION_TOKEN="<token from the OAuth device flow>"
```

> **Keep tokens secret**
>
> Each of these acts on someone's behalf, so treat them like passwords. Neither is
> public-facing: never put one in a browser bundle, a mobile or desktop app,
> client-side code, or a committed file (git). Keep them in environment variables, which
> is why every sample below reads them from the environment. See
> [keeping the tokens safe](/docs/api/authentication#keeping-the-tokens-safe).

## 2. Confirm your token works

The fastest check is to ask the API who you are.

cURL:

```bash
curl https://api2.rundiffusion.com/api/v2/me \
  -H "Authorization: Bearer $RUNDIFFUSION_TOKEN"
```

JavaScript:

```javascript
const me = await fetch('https://api2.rundiffusion.com/api/v2/me', {
  headers: { Authorization: `Bearer ${process.env.RUNDIFFUSION_TOKEN}` },
}).then(r => r.json());

console.log(me);
```

Python:

```python
import os
import requests

BASE = "https://api2.rundiffusion.com/api/v2"
HEADERS = {"Authorization": f"Bearer {os.environ['RUNDIFFUSION_TOKEN']}"}

me = requests.get(f"{BASE}/me", headers=HEADERS).json()
print(me)
```

A `401` here means the token is wrong or expired. Anything else and you are ready
to continue.

Keep the `accounts` array from that response. Each entry has an `id`, and it tells
you what to send on account-scoped requests: a team's `id` goes in the `team_id`
query parameter, and sending no `team_id` acts on the caller's individual
account. See [Identity](/docs/api/me).

## 3. List what your account holds

The library is the generations on your account, newest first. One request gets you a
page of it.

cURL:

```bash
curl -G https://api2.rundiffusion.com/api/v2/library \
  -H "Authorization: Bearer $RUNDIFFUSION_TOKEN" \
  --data-urlencode "limit=24"
```

JavaScript:

```javascript
const page = await fetch(
  'https://api2.rundiffusion.com/api/v2/library?limit=24',
  { headers: { Authorization: `Bearer ${process.env.RUNDIFFUSION_TOKEN}` } },
).then(r => r.json());

for (const item of page.data) {
  console.log(item.id, item.type, item.created);
}
```

Python:

```python
page = requests.get(
    f"{BASE}/library",
    headers=HEADERS,
    params={"limit": 24},
).json()

for item in page["data"]:
    print(item["id"], item["type"], item["created"])
```

Each item carries its `type`, its dimensions, and a signed `url` you can download.
Those URLs expire after roughly seven days, so treat them as short-lived and
re-list rather than storing them. See [Library](/docs/api/library/list).

## 4. Pull the usage behind it

Reporting answers the other half: what that activity cost. It returns one row per
balance record rather than a pre-aggregated total, so you can group it however your
business actually works.

Note the different credential here.

cURL:

```bash
curl -G https://api2.rundiffusion.com/api/v2/reporting/token-usage \
  -H "Authorization: Bearer $RUNDIFFUSION_API_TOKEN" \
  --data-urlencode "start_utc=2026-07-01T00:00:00Z" \
  --data-urlencode "end_utc=2026-08-01T00:00:00Z" \
  --data-urlencode "limit=1000"
```

JavaScript:

```javascript
const params = new URLSearchParams({
  start_utc: '2026-07-01T00:00:00Z',
  end_utc: '2026-08-01T00:00:00Z',
  limit: '1000',
});

const usage = await fetch(
  `https://api2.rundiffusion.com/api/v2/reporting/token-usage?${params}`,
  { headers: { Authorization: `Bearer ${process.env.RUNDIFFUSION_API_TOKEN}` } },
).then(r => r.json());

const total = usage.data.reduce((sum, row) => sum + row.total_tokens, 0);
console.log(usage.data.length, 'rows,', total, 'tokens');
```

Python:

```python
REPORTING_HEADERS = {
    "Authorization": f"Bearer {os.environ['RUNDIFFUSION_API_TOKEN']}"
}

usage = requests.get(
    f"{BASE}/reporting/token-usage",
    headers=REPORTING_HEADERS,
    params={
        "start_utc": "2026-07-01T00:00:00Z",
        "end_utc": "2026-08-01T00:00:00Z",
        "limit": 1000,
    },
).json()

total = sum(row["total_tokens"] for row in usage["data"])
print(len(usage["data"]), "rows,", total, "tokens")
```

> **Reporting is company-wide**
>
> Reporting returns usage for the whole company a token is scoped to, not for
> one user. That is why it requires an Enterprise plan, and why the token's owner
> also needs the **view company reports** permission: the token acts as them, and
> the numbers are the company's.
>
> It is also the one endpoint with no account selection at all: the Company API
> Access Token names its company already, so there is no `team_id` to send. A
> personal token and an OAuth token are both refused here. See
> [Authorization](/docs/api/reporting/token-usage#authorization).

## 5. Page through everything

Both endpoints use the same cursor pattern, so one helper covers both: follow
`next_cursor` until `has_more` is false.

JavaScript:

```javascript
async function* allPages(url, headers, params = {}) {
  let cursor;

  while (true) {
    const query = new URLSearchParams({ ...params });
    if (cursor) query.set('cursor', cursor);

    const page = await fetch(`${url}?${query}`, { headers }).then(r => r.json());
    yield* page.data;

    if (!page.has_more || !page.next_cursor) return;
    cursor = page.next_cursor;
  }
}

const headers = { Authorization: `Bearer ${process.env.RUNDIFFUSION_API_TOKEN}` };

for await (const row of allPages(
  'https://api2.rundiffusion.com/api/v2/reporting/token-usage',
  headers,
  { start_utc: '2026-07-01T00:00:00Z', end_utc: '2026-08-01T00:00:00Z' },
)) {
  console.log(row.record_id, row.total_tokens);
}
```

Python:

```python
def all_pages(path, headers, params=None):
    params = dict(params or {})
    cursor = None

    while True:
        if cursor:
            params["cursor"] = cursor

        page = requests.get(f"{BASE}{path}", headers=headers, params=params).json()
        yield from page["data"]

        cursor = page.get("next_cursor")
        if not page.get("has_more") or not cursor:
            return

for row in all_pages(
    "/reporting/token-usage",
    REPORTING_HEADERS,
    {"start_utc": "2026-07-01T00:00:00Z", "end_utc": "2026-08-01T00:00:00Z"},
):
    print(row["record_id"], row["total_tokens"])
```

> **Page with next_cursor, not last_cursor**
>
> `last_cursor` is an echo of the cursor **you sent**, so it is `null` on the first
> page. The cursor for the *next* page is `next_cursor`. Paging on `last_cursor`
> stops after one page.

> **Ask for larger pages**
>
> Reporting accepts up to 100,000 rows per request, and every request costs the
> same against your rate limit, so one large page beats twenty small ones. See
> [Rate limits](/docs/api/rate-limits).

## Where to go next

- [Reporting](/docs/api/reporting/token-usage) for every filter, the full row shape, and how
  teams wire it into business intelligence tools.
- [Library](/docs/api/library/list) for the filters that narrow a large library.
- [Errors](/docs/api/errors) for the codes worth handling explicitly.
- [Identity](/docs/api/me) for account selection and permissions.
