Skip to content
pokemontcgapi.com
Documentation

Migrating from pokemontcg.io to pokemontcgapi

Adapt your card IDs, response parsing, pagination and price reads to https://api.pokemontcgapi.com/v1.

This guide is for an application with an existing Pokémon card integration or saved dataset. The mappings below describe how to handle fields your application may already use. They document the destination API; they are not an inventory of another provider’s current features.

Read at the source before you plan anything. As of 16 September 2026, the other API’s own documentation states: “The Pokémon TCG API is deprecated. New account registrations are no longer available. Existing API keys will continue to function through March 1, 2027. Please migrate your application to Scrydex.” That is their announcement, quoted in full, not our reading of their plans. Check the page yourself: it is the only version that stays current.

Start with a small set of saved cards

Get your own key from Free API key, then send it in the X-Api-Key header. Keep the key in your backend or another trusted environment.

# Set PTCG_API_KEY to your own key before running these examples.
curl --fail-with-body --get \
  'https://api.pokemontcgapi.com/v1/cards/base1-4' \
  --header "X-Api-Key: ${PTCG_API_KEY}" \
  --data-urlencode 'include=images,set'

This catalog request costs 1 credit. It requests the image and set relations explicitly. Read the returned card directly from the top-level JSON object, without a data wrapper; use its returned id as the canonical ID in your new integration.

Test the IDs and fields that your application actually depends on before importing the rest of your collection. Keep unresolved records visible in a review queue.

Resolve IDs before changing your stored references

Our card object has an id and a nullable legacy_id. Single-card and batch lookups accept either value when that alias exists in our catalog. For example, the documented card bs-4 has the legacy ID base1-4.

Our set object has code, slug and a nullable legacy_id. The set’s id equals its code. Set lookup accepts these identifiers; for example, bs, base and base1 resolve the documented Base Set record.

An alias is a recorded relationship, not a string-rewriting rule. Do not generate new IDs by replacing a prefix. There is no guaranteed mapping for every ID in an existing dataset. If lookup does not resolve a record, compare set, collector number, print region and imagery before linking it manually. A card name alone is insufficient.

Keep the original provider ID alongside the canonical ID returned here. This lets you audit a match without changing what the original record meant.

Translate the fields your application reads

The left column describes a field in an existing saved record or adapter. Apply a row only when that field is present in your input.

Existing field or conceptRead from pokemontcgapiHandling
Card idid, optionally legacy_idResolve the stored ID, then preserve the returned canonical ID.
name, number, supertype, subtypes, types, raritySame field namesKeep number as a string and preserve missing values.
hphpA nullable integer; do not turn an absent value into zero.
evolvesFrom, evolvesToevolves_from, evolves_toRead the destination fields explicitly.
regulationMark, flavorTextregulation_mark, flavor_textBoth can be null.
retreatCost, convertedRetreatCostretreat_cost, converted_retreat_costEnergy-symbol array and nullable integer.
nationalPokedexNumbersnational_pokedex_numbersInteger array.
Attack convertedEnergyCostAttack converted_cost inside attacksAttack damage remains a string; do not coerce values containing operators into numbers.
A string artistartist_name, artist_sluginclude=artist requests a separate object.
Nested set identity and nameset_code, set_name; set with include=setResolve the ID separately from the name.
Set printedTotal, ptcgoCode, releaseDate, updatedAtprinted_total, ptcgo_code, release_date, updated_at on the setrelease_date is a date; updated_at is a timestamp.
Set image fieldslogo_url, symbol_url on the setThese fields are nullable; keep an image fallback.
images.small, images.largeimages[] with include=imagesInspect each entry’s url, face, size and locale. There is no fixed small/large object mapping.
Provider-specific nested price blocksprices[] with include=prices, or data.quotes[] on the current-price routeRebuild the price-selection logic around the quote dimensions below. There is no universal one-field rename.
legalitiesNo equivalent relationDo not substitute regulation_mark for format legality. include=legalities is not supported.

Relations are omitted unless requested. A missing relation is different from a requested relation containing an empty array. The card object and set object describe the full destination schema and its nullable fields.

Replace the pagination loop

Collections return a {data, meta, links} envelope and use limit and an opaque cursor. meta.limit records the requested page size; links.next sits alongside meta, not inside it. The default limit is 50, with a maximum of 250. Follow the complete links.next URL until it is absent. Read rows from data and the current page’s row count from meta.count; meta.has_more indicates whether another page exists.

A stored page or pageSize value does not translate into a cursor. Start the destination query again. Card list responses do not provide a total-count equivalent for every query.

curl --fail-with-body --get \
  'https://api.pokemontcgapi.com/v1/cards' \
  --header "X-Api-Key: ${PTCG_API_KEY}" \
  --data-urlencode 'set=base1' \
  --data-urlencode 'q=name:charizard' \
  --data-urlencode 'include=images' \
  --data-urlencode 'limit=50'

This request costs 1 credit. A set alias in the set parameter is resolved against our catalog. Revalidate saved search expressions against query syntax; do not assume every expression from an existing client has an equivalent. orderBy uses our destination field names, such as orderBy=-updated_at.

For the paging protocol, see Pagination. A collection cursor represents a position in an ordering, not a frozen snapshot or a durable synchronization checkpoint.

Read prices without changing their meaning

curl --fail-with-body --get \
  'https://api.pokemontcgapi.com/v1/cards/base1-4/prices' \
  --header "X-Api-Key: ${PTCG_API_KEY}"

This request costs 2 credits. The response separates data.index from data.quotes.

A quote carries amount, currency, variant, basis, locale, condition, printing, grading, as_of and provenance. Some dimensions can be null. Choose the dimensions your application requires before displaying or aggregating a value. Our variant describes a price measure; printing describes a physical printing. They are not interchangeable.

EUR is represented explicitly by currency: "EUR". Our composite value is also available as the nullable index_eur field. It is not a renamed lowest offer, last sale or arithmetic average from your previous dataset. Preserve the currency and the observation date; an empty quote array or null index means no available value for that response, not a value of zero.

On card lists and batches, request include=index for the composite fields or include=prices for quotes. Plain catalog lists omit the priced index fields. Price responses are subject to availability, delay rules and the account’s plan. See Price methodology before defining a valuation rule.

Add the destination resources your application needs

These are optional parts of the pokemontcgapi model, not claims of exclusivity relative to another service.

Japanese and Chinese print lines. Discover sets with GET /v1/sets?region=JP or GET /v1/sets?region=CN, then read cards through GET /v1/sets/{code}/cards. Each plain catalog request costs 1 credit. Card print_region and set region identify the print line. lang selects a localized name with a fallback when the requested translation is unavailable; it does not turn a Western printing into a Japanese or Chinese card. A nullable jp_twin_id links a Japanese counterpart where a pairing is recorded. There is no complete automatic cross-region mapping.

Sealed products. GET /v1/sealed lists products; GET /v1/sealed/{id} reads one. Both plain catalog calls cost 1 credit. The product’s id equals its sku. Fields include name, kind, set_code, image_url and languages. Use the SKU as a product identity, not a card number. GET /v1/sealed/{id}/prices costs 2 credits. A product catalog and its quotes do not provide retailer stock or restock alerts.

Incremental updates. GET /v1/changes costs 1 credit per call, including an empty successful response. The feed returns change events, not complete card or price records; fetching affected records costs additional credits.

For an initial replica, read the feed with since=0&limit=1 and capture meta.watermark before importing the catalog. Complete the catalog import, then consume changes after that captured watermark so changes made during the import are replayed. If the checkpoint expires during this process, restart the import.

For ongoing synchronization, apply each page’s changes and successfully fetch the required records before committing meta.next_since as your next since value. Follow meta.has_more, handle deletions, and make application of repeated events idempotent. The change feed’s limit defaults to 250 and can reach 1,000. An old checkpoint can return 410 `SYNC_CURSOR_TOO_OLD`: the retained history can no longer reconstruct the delta. Perform a fresh import with a new baseline. There is no promise here of an unlimited retention window.

Budget the requests you will actually make

The following costs apply to the routes used in this guide. ceil means round up. List pricing uses the requested limit, not the number of rows eventually returned; batch pricing uses the requested ID count. Request limits still apply.

RouteRequest shapeCredits
GET /v1/cardsPlain catalog, including images or set metadata1
GET /v1/cardsinclude=indexceil(limit / 50)
GET /v1/cardsinclude=prices4 × ceil(limit / 25)
GET /v1/cards/{id}Plain catalog, including images or set metadata1
GET /v1/cards/{id}include=prices2
GET /v1/cards/batchPlain catalogceil(requested IDs / 25)
GET /v1/cards/batchinclude=indexceil(requested IDs / 50)
GET /v1/cards/batchinclude=prices4 × ceil(requested IDs / 25)
GET /v1/setsCatalog1
GET /v1/sets/{code}Catalog1
GET /v1/sets/{code}/cardsPlain catalog1
GET /v1/sets/{code}/cardsinclude=indexceil(limit / 50)
GET /v1/sets/{code}/cardsinclude=prices4 × ceil(limit / 25)
GET /v1/cards/{id}/pricesCurrent quotes2
GET /v1/sealedPlain catalog1
GET /v1/sealedinclude=indexceil(limit / 50)
GET /v1/sealed/{id}Catalog1
GET /v1/sealed/{id}/pricesCurrent quotes2
GET /v1/changesA page of events1
GET /v1/billing/plansPublic plan listing, no key required0

An include price is the total route cost for that request, not a surcharge added to the plain catalog cost. On cards, asking for both index and prices uses the prices tariff. A catalog batch accepts up to 100 IDs.

For example, a card list requesting limit=250 costs 1 credit for plain catalog data, 5 credits with include=index, or 40 credits with include=prices. A smaller final response does not reduce the cost calculated from the requested limit.

Plans

Public list prices and allowances:

PlanMonthly billingAnnual billingCredits
TrialFreeNot a subscription800 once, after email verification
Developer29 €/month290 €/year50,000 per month
Growth79 €/month790 €/year200,000 per month
Professional149 €/month1,490 €/year500,000 per month

The trial expires 30 days after signup and has a 400-credit daily cap after verification. Its allowance does not renew. Before verification, signup limits are reduced. Trial quotes exclude graded observations and observations explicitly labeled with a non-English locale; observations without a locale label can still be returned. Catalog print-region coverage is a separate dimension from those quote permissions.

Developer includes all quote locales; graded quotes require Growth or above. These permissions do not create data for an unquoted card or product. Check Pricing or the public GET /v1/billing/plans response for the current list before budgeting a deployment.

Credit allowance, request rate and concurrency are separate limits. Respect Retry-After when returned, cap your concurrency, and read X-Credits-Cost to inspect request charges. See Rate limits for the account limits and Caching and ETags for conditional requests.

Check the adapter before switching traffic

Validate known cards, unresolved IDs, missing images, unquoted cards, the currencies you use and the print regions you support. Compare the meaning of each displayed price, not just its numeric value. Exercise a multi-page import and restart the sync worker from a saved checkpoint. Keep your original identifiers and stored observations until you have verified the new mapping.

Start with Quickstart, then use the API reference for the routes your application needs.

view this page as markdown