# Quickstart

> Get a key, make your first request, and page a full set correctly — in about three minutes.

Source: https://pokemontcgapi.com/docs/quickstart

Three steps. The only prerequisite is something that can send an HTTP header.

## 1. Get a key

1. Ask for a key at [pokemontcgapi.com/free-api-key](https://pokemontcgapi.com/free-api-key). No card, no call.
2. Copy the key from the dashboard. It is shown once — we store an Argon2 hash, not the key, so we cannot show it to you again.
3. Put it in your environment as `PTCG_API_KEY`.

> **Secret keys are server-side**
>
> A key starting `ptcg_live_` belongs on a server. For browser code use a `ptcg_pub_` key, which is catalogue-only and pinned to a referrer allow-list. See [authentication](https://pokemontcgapi.com/docs/authentication).

## 2. Make a request

Search is the endpoint you will use most. This asks for the two most recent Charizard holo rares:

**curl**

```bash
curl -s -G "https://api.pokemontcgapi.com/v1/cards" \
  --data-urlencode 'q=name:charizard rarity:"rare holo"' \
  --data-urlencode "orderBy=-release_date" \
  --data-urlencode "limit=2" \
  -H "X-Api-Key: $PTCG_API_KEY"
```

**TypeScript**

`quickstart.ts`

```ts
const url = new URL("https://api.pokemontcgapi.com/v1/cards");
url.searchParams.set("q", 'name:charizard rarity:"rare holo"');
url.searchParams.set("orderBy", "-release_date");
url.searchParams.set("limit", "2");

const res = await fetch(url, {
  headers: { "X-Api-Key": process.env.PTCG_API_KEY ?? "" },
});

if (!res.ok) {
  const { error } = await res.json();
  throw new Error(`${error.code}: ${error.message} (${error.request_id})`);
}

const { data, meta } = await res.json();
console.log(meta.count, data[0]?.name);
```

**Python**

`quickstart.py`

```python
import os, httpx

res = httpx.get(
    "https://api.pokemontcgapi.com/v1/cards",
    params={"q": 'name:charizard rarity:"rare holo"', "orderBy": "-release_date", "limit": 2},
    headers={"X-Api-Key": os.environ["PTCG_API_KEY"]},
)
res.raise_for_status()
payload = res.json()
print(payload["meta"]["count"], payload["data"][0]["name"])
```

Collections always come back in the same envelope:

`envelope.json`

```json
{
  "data": [ /* rows */ ],
  "meta": { "limit": 2, "count": 2, "has_more": true },
  "links": { "next": "https://api.pokemontcgapi.com/v1/cards?...&cursor=eyJrIjpb..." }
}
```

## 3. Page a whole set

The only correct way to page is to follow `links.next` until it disappears. Do not rebuild the URL, do not add a `page` parameter — there is not one — and do not change `orderBy` mid-run.

`page-a-set.ts`

```ts
const key = process.env.PTCG_API_KEY ?? "";
let next: string | null = "https://api.pokemontcgapi.com/v1/sets/obf/cards?limit=250";
const all: unknown[] = [];

while (next) {
  const res = await fetch(next, { headers: { "X-Api-Key": key } });
  if (!res.ok) throw new Error((await res.json()).error.code);

  const page = await res.json();
  all.push(...page.data);
  next = page.links?.next ?? null;
}

// 230 on 2026-08-27, each id exactly once. obf sits just past the 250
// ceiling, so it is the smallest set that actually exercises the cursor.
console.log(all.length);
```

## What to read next

The [query syntax](https://pokemontcgapi.com/docs/query-syntax) page is the one that pays for itself: most of what people ask support is already expressible in `q`. After that, [caching and ETags](https://pokemontcgapi.com/docs/caching-etags) — a 304 costs no credits, so a well-behaved client is a cheap client.

> **What the trial gives you**
>
> Five requests per second, 800 credits granted once, and a daily cap of 200. The credits do not renew: they exist to answer "does this API have what I need", and most calls spend one of them. [Rate limits](https://pokemontcgapi.com/docs/rate-limits) has the cost of the ones that spend more.
