Collections

List Collections

GET/api/v1/collections
Scopecollections:read
AuthBearer token
PaginatedCursor
Rate limit60 / minute

Returns a paginated list of the user's card collections.

ParameterRequiredDescription
limitNoNumber of results (1-100, default 20)
cursorNoCursor from previous response for pagination
Response
{
  "data": [
    {
      "id": "550e8400-...",
      "name": "My Collection",
      "card_count": 1247,
      "created_at": "2026-02-10T08:00:00Z"
    }
  ],
  "meta": {
    "request_id": "req_xyz",
    "timestamp": "2026-03-31T14:22:00Z",
    "has_more": false,
    "next_cursor": null
  }
}

Get Collection

GET/api/v1/collections/{id}
Scopecollections:read
AuthBearer token
PaginatedCursor over cards
Rate limit60 / minute

Returns a collection with a page of its cards. Follow next_cursor until has_more is false to read the whole collection. card_count is the total for the collection, not the page. Cards are ordered by oracle_id, which is stable across pages; sort client-side if you need them alphabetical.

ParameterRequiredDescription
limitNoCards per page (1-500, default 100)
cursorNonext_cursor from the previous response. A malformed cursor returns 400 invalid_cursor rather than silently restarting at the first page.
colorNoComma-separated colours: W, U, B, R, G, or C for colourless. Values are OR-ed, so color=W,U returns cards playable with white or blue available. C matches cards with no colour and cannot be expressed by listing the other five.
color_matchNoidentity (default) matches colour identity, which is what Commander legality uses. colors matches the card's own casting colours. They differ: a land with a coloured ability has no casting colour but does have a colour identity. Sending color_match without color returns 400.
typeNoComma-separated card types: artifact, battle, creature, enchantment, instant, kindred, land, planeswalker, sorcery. Values are OR-ed. Matches the FRONT face and the types only, so a Saga that transforms into a creature is an enchantment, not a creature, and an Aura that enchants a creature is not one either. Subtypes are not accepted; type=wizard returns 400.

An unrecognised filter value returns 400 invalid_filter naming the field and listing what is supported, rather than being ignored. A silently dropped value would return a page that quietly answered a different question. card_count is always the whole collection and does not shrink when a filter is applied, so meta.filters echoes back what was actually filtered.

Response
{
  "data": {
    "id": "550e8400-...",
    "name": "My Collection",
    "card_count": 1247,
    "created_at": "2026-02-10T08:00:00Z",
    "cards": [
      {
        "oracle_id": "a1b2c3d4-...",
        "name": "Sol Ring",
        "mana_cost": "{1}",
        "type_line": "Artifact",
        "colors": [],
        "color_identity": [],
        "quantity": 3
      }
    ]
  },
  "meta": {
    "request_id": "req_xyz",
    "timestamp": "2026-03-31T14:22:00Z",
    "has_more": true,
    "next_cursor": "6c1c4c1a-9c39-4b4e-9f8f-2f6f1a0d5b21",
    "filters": { "color": ["G"], "color_match": "identity", "type": ["creature"] }
  }
}

Create Collection

POST/api/v1/collections
Scopecollections:write
AuthBearer token
Request bodyJSON
Cards per request10,000 max
Rate limit60 / minute

Creates a new collection, optionally with cards. Respects your plan's collection limit.

HeaderRequiredDescription
Idempotency-KeyNoAny string of 1-255 printable ASCII characters, unique per distinct request (a UUID works). A retry with the same key and the same body returns the stored response with Idempotent-Replayed: true instead of importing again. The same key with a different body returns 422 idempotency_key_reused. Keys expire after 24 hours.
Request
{
  "name": "Gestic Import - March 2026",
  "cards": [
    { "oracle_id": "0004ebd0-dfd6-4276-b4a6-de0003e94237", "quantity": 2 },
    { "name": "Sol Ring", "quantity": 3 },
    { "name": "Fire // Ice", "quantity": 1 }
  ]
}
Response (201)
{
  "collection_id": "550e8400-...",
  "collection_name": "Gestic Import - March 2026",
  "added": 2,
  "updated": 1,
  "errors": [],
  "capacity": { "used": 1250, "limit": 20000, "remaining": 18750 }
}

If the collection name is omitted, a default name is generated. If the card list cannot be imported at all (for example, the collection limit is reached), no collection is created.

Identify each card by oracle_id (preferred: exact, and cards that share a name are never merged) or by name. If a row has both, the card is matched by oracle_id; the row's other checks, such as the name length limit, still apply. An empty oracle_id ("" or null) is treated as not sent, so the row is matched by name. Names for split, adventure and double-faced cards are accepted in full ("Fire // Ice") or as either face ("Fire"). If a card appears more than once in a request, its quantities are added together.

Per-row error reasons, each with the row's index: not_found (name not recognized, or oracle_id unknown), invalid_oracle_id (oracle_id is not a UUID), capacity_limit (plan limit reached), invalid_quantity (quantity must be 1-1000), missing_identifier (no name or oracle_id provided), name_too_long (over 200 characters). Request-level errors: 400 batch_too_large (over 10,000 cards), 400 invalid_idempotency_key, 409 import_in_progress (another import for the same user is still running; retry after the Retry-After header), 422 idempotency_key_reused.

Import Cards

POST/api/v1/collections/{id}/cards
Scopecollections:write
AuthBearer token
Request bodyJSON
Cards per request10,000 max
Rate limit60 / minute

Adds cards to an existing collection. If a card already exists, its quantity is incremented (upsert). Respects your plan's card capacity limit. Cards that exceed the limit are skipped.

HeaderRequiredDescription
Idempotency-KeyNoAny string of 1-255 printable ASCII characters, unique per distinct request (a UUID works). A retry with the same key and the same body returns the stored response with Idempotent-Replayed: true instead of importing again. The same key with a different body returns 422 idempotency_key_reused. Keys expire after 24 hours.
Request
{
  "cards": [
    { "name": "Lightning Bolt", "quantity": 4 },
    { "name": "Nonexistent Card", "quantity": 1 }
  ]
}
Response
{
  "collection_id": "550e8400-...",
  "added": 1,
  "updated": 0,
  "errors": [
    { "index": 1, "name": "Nonexistent Card", "reason": "not_found" }
  ],
  "capacity": { "used": 1251, "limit": 20000, "remaining": 18749 }
}

Identify each card by oracle_id (preferred: exact, and cards that share a name are never merged) or by name. If a row has both, the card is matched by oracle_id; the row's other checks, such as the name length limit, still apply. An empty oracle_id ("" or null) is treated as not sent, so the row is matched by name. Names for split, adventure and double-faced cards are accepted in full ("Fire // Ice") or as either face ("Fire"). If a card appears more than once in a request, its quantities are added together.

Per-row error reasons, each with the row's index: not_found (name not recognized, or oracle_id unknown), invalid_oracle_id (oracle_id is not a UUID), capacity_limit (plan limit reached), invalid_quantity (quantity must be 1-1000), missing_identifier (no name or oracle_id provided), name_too_long (over 200 characters). Request-level errors: 400 batch_too_large (over 10,000 cards), 400 invalid_idempotency_key, 409 import_in_progress (another import for the same user is still running; retry after the Retry-After header), 422 idempotency_key_reused.

Importing Large Collections

Send up to 10,000 cards per request. A 10,000-card request typically completes in a few seconds, so a 50,000-card collection is a handful of calls: create the collection with the first batch, then send the rest to /collections/{id}/cards.

  • Send batches one after another. Imports for the same user run one at a time on our side so the capacity check stays exact. Parallel requests for one user wait for each other and do not finish sooner, and a request that waits more than a few seconds returns 409 import_in_progress with nothing written.
  • Use an Idempotency-Key per batch. Quantities are additive, so retrying a request that actually succeeded would count those cards twice. With a key, the retry returns the original response instead.
  • Map errors with index. Each error's index is the position in that request's cards array, starting at 0 for every request. It is not a position in your full list.
  • Check capacity. The plan limit counts distinct cards across all of the user's collections. Rows past it come back as capacity_limit.