# Cardfight!! Vanguard API Documentation

Source: https://pokewallet.io/api-docs/cardfight-vanguard

REST reference for the Cardfight!! Vanguard API: sets, cards, search and images, with prices from TCGPlayer and CardMarket. Same API key as the Pokémon and One Piece APIs.

In short

The Cardfight!! Vanguard API returns 420 sets with their cards, search and images, and TCGPlayer (USD) and CardMarket (EUR) prices refreshed daily. These endpoints are free (100 requests/hour) with the same key as the other 11 games; prices by set and set analytics need the Ultra plan (€29/month) or its 7-day trial.

At a glance

- **Base path:** `https://api.pokewallet.io/cfv`
- **Sets:** 420 Cardfight!! Vanguard sets
- **Card IDs:** `cfv_` + hex for cards on TCGPlayer; 64 hex characters for CardMarket-only cards
- **Prices:** TCGPlayer (USD) for every finish and CardMarket (EUR) for `normal` and `foil`, refreshed daily
- **Free on every plan:** Sets, set cards, card details, search and images
- **Ultra plan or 7-day trial:** Prices by set, set statistics, completion value, trending sets and top cards
- **API key:** The same key as the Pokémon and One Piece APIs, with shared rate limits

Updated Oct 19, 2026

## Quick Start

Get started with PokéWallet API in 4 simple steps:

### Create an Account

Sign up for free to start using the API. Upgrade anytime from your dashboard.

[Sign Up Now](https://pokewallet.io/auth/login?source=pokemon)

### Generate an API Key

Create your first API key from the dashboard.

### Make Your First Request

Start querying the API with your key.

*cURL*

```bash
curl -H "X-API-Key: pk_live_your_key_here" \
       "https://api.pokewallet.io/cfv/sets"
```

### Join the Discord Community

Stay up to date with new features, ask for help, and connect with other developers.

[Join Discord](https://discord.gg/znDxEjmDVP)

## Authentication

All API requests (except `/health`) require authentication using an API key.

### API Key Format

API keys follow this format:

- **Production keys:** `pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`
- **Development keys:** `pk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`

> **Security Warning:** Never expose your API keys in client-side code, public repositories, or logs. Always use environment variables.

### Authentication Methods

You can authenticate using either the `X-API-Key` header or `Authorization` header:

*X-API-Key Header (Recommended)*

```bash
curl -H "X-API-Key: pk_live_your_key_here" \
     "https://api.pokewallet.io/search?q=pikachu"
```

*Authorization Header*

```bash
curl -H "Authorization: Bearer pk_live_your_key_here" \
     "https://api.pokewallet.io/search?q=pikachu"
```

## Rate Limits

Rate limits vary depending on your subscription plan:

| Plan | Per hour | Per day | Paid endpoints | Price |
| --- | --- | --- | --- | --- |
| Free | 100 | 1,000 | — | €0 |
| Coffee | 1,000+ | 10,000+ | — | Buy a coffee on Ko-fi |
| Pro | 5,000 | 50,000 | Pokémon + One Piece | €20/month |
| Ultra | 7,000 | 70,000 | All 12 games | €29/month |
| Business | Custom | Custom | All 12 games | Contact us |
| Early Access | 1,000 | 10,000 | — | No longer available |

> All new users start on the **Free plan**. Upgrade from your [dashboard](https://pokewallet.io/dashboard). Early Access was our initial launch tier and is no longer available for new users.

> **Please note:** After upgrading your plan, activation may require a couple of hours.

[See all plans, pricing and FAQs](https://pokewallet.io/pricing)

### Rate Limit Headers

Every API response includes rate limit information:

```
X-RateLimit-Limit-Hour: 100
X-RateLimit-Remaining-Hour: 95
X-RateLimit-Limit-Day: 1000
X-RateLimit-Remaining-Day: 823
```

### Rate Limit Exceeded (429)

When you exceed your rate limit, you'll receive a `429 Too Many Requests` response:

```json
{
  "error": "Rate limit exceeded",
  "message": "Hourly limit exceeded",
  "limits": {
    "hourly": {
      "limit": 100,
      "used": 100,
      "remaining": 0
    },
    "daily": {
      "limit": 1000,
      "used": 523,
      "remaining": 477
    }
  }
}
```

## Public Endpoints

> **No Authentication Required:** These endpoints are publicly accessible.

### GET /health (No Auth Required)

Check API status, database, cache, and storage health with response times.

### Example Request:

*cURL*

```bash
curl "https://api.pokewallet.io/health"
```

### Response Example (Healthy):

*200 OK*

```json
{
  "status": "healthy",
  "timestamp": "2025-12-16T10:30:00.000Z",
  "version": "1.1.0",
  "checks": {
    "database": {
      "status": "healthy",
      "responseTime": 45
    },
    "cache": {
      "status": "healthy",
      "responseTime": 3
    },
    "storage": {
      "status": "healthy",
      "responseTime": 12
    }
  },
  "responseTime": 60,
  "region": "FRA"
}
```

### Response Example (Unhealthy):

*503 Service Unavailable*

```json
{
  "status": "unhealthy",
  "timestamp": "2025-12-16T10:30:00.000Z",
  "version": "1.1.0",
  "message": "Critical services unavailable - API may not function correctly",
  "checks": {
    "database": {
      "status": "unhealthy",
      "responseTime": null,
      "message": "Connection failed"
    },
    "cache": {
      "status": "healthy",
      "responseTime": 3
    },
    "storage": {
      "status": "healthy",
      "responseTime": 12
    }
  },
  "responseTime": 1050,
  "region": "FRA"
}
```

### GET / (No Auth Required)

Get API information, version, and available endpoints.

### Example Request:

*cURL*

```bash
curl "https://api.pokewallet.io/"
```

### Response Example:

*200 OK*

```json
{
  "name": "PokeWallet API",
  "version": "1.1.0",
  "status": "production",
  "authentication": {
    "required": true,
    "method": "API Key",
    "header": "X-API-Key",
    "format": "pk_live_xxxxxxxxxxxxx"
  },
  "endpoints": {
    "health": { "path": "/health", "auth_required": false },
    "search": { "path": "/search?q=charizard&limit=20", "auth_required": true },
    "cardDetail": { "path": "/cards/:id", "auth_required": true },
    "sets": { "path": "/sets", "auth_required": true },
    "setDetails": { "path": "/sets/:setCode?page=1&limit=50", "auth_required": true },
    "images": { "path": "/images/:id?size=high", "auth_required": true }
  },
  "rate_limits": {
    "per_hour": 100,
    "per_day": 1000,
    "plan": "free"
  }
}
```

### GET /games (No Auth Required)

Lists the games available under their own prefix (`/mtg`, `/ygo`, …) with their routes. Pokémon and One Piece are not in this list: they use `/cards`, `/sets`… and `/op/…`.

### Example Request:

*cURL*

```bash
curl "https://api.pokewallet.io/games"
```

### Response Example (first of ten games):

*200 OK*

```json
{
  "success": true,
  "data": [
    {
      "code": "mtg",
      "name": "Magic: the Gathering",
      "endpoints": {
        "sets": "/mtg/sets",
        "set": "/mtg/sets/:set",
        "card": "/mtg/cards/:id",
        "search": "/mtg/search?q=",
        "image": "/mtg/images/:id"
      }
    }
  ]
}
```

> **Cardfight!! Vanguard PRO endpoints: Ultra plan required**
>
> The **Pro plan does not include them**: Pro covers Pokémon and One Piece only. Ultra (€29/month) includes everything in Pro plus the paid endpoints of **all 12 games**. Free endpoints (sets, cards, search, images) work on every plan.
>
> [Compare plans →](https://pokewallet.io/pricing)

## Cardfight!! Vanguard

All Cardfight!! Vanguard endpoints live under `/cfv`. Same API key, same rate limits and the same response format as the Pokémon endpoints.

### Card IDs

| Format | Example | Used for |
| --- | --- | --- |
| `cfv_` + hex | `cfv_5277c3b17c4cf57bd911…` | Every card with a TCGPlayer product, even if it also has CardMarket data |
| 64 hex characters, no prefix | `e26f12b8b1fabf19…` | Cards that exist only on CardMarket |

> A `cfv_…` ID only works under `/cfv/`. Finishes of the same product (Normal, Foil, …) are one card with one ID; each finish has its own TCGPlayer price. If you request a card by its CardMarket ID and it is linked to a TCGPlayer product, the response uses the canonical `cfv_…` ID.

### Response Format

Same shape as the Pokémon API: `card_info`, `images`, `tcgplayer.prices` and `cardmarket.prices` as arrays, `pagination`, errors as `{ "error", "message" }`. Differences:

| Field | Cardfight!! Vanguard |
| --- | --- |
| Game fields in `card_info` | Instead of Pokémon's `hp`, `stage`, `attacks`…, the game's own fields in snake_case (present when the card has them): `grade`, `power`, `shield`, `critical`, `unit`, `skill_icon`, `nation`, `race`, `flavor_text`, `clan`, `trigger`, `imaginary_gift`, `upc` |
| `card_type`, `card_text` | Filled from the game's card type and rules text. `card_text` may contain HTML (`<strong>`, `<em>`…) as provided by TCGPlayer. |
| `card_info.variants`, `card_info.set_id` | Always present, on every endpoint |
| `product_type` | `"card"`, `"sealed"` or `null` when unknown — see [Data Notes](https://pokewallet.io/api-docs/cardfight-vanguard#cfv-data-notes) |
| `release_date` | ISO format, e.g. `"2025-03-07T00:00:00"` (Pokémon uses `"16th June, 2023"`) |
| `images.languages` | `["en"]`, or `[]` when there is no image |

## Cardfight!! Vanguard Endpoints

### GET /cfv/sets

All Cardfight!! Vanguard sets.

### Response Fields:

| Field | Type | Description |
| --- | --- | --- |
| `name` | string | Set name |
| `set_code` | string \| null | Short code. **Not unique**: several sets can share it. |
| `set_id` | string | Unique set identifier. Negative for sets that exist only on CardMarket. |
| `card_count` | integer | Cards in the set (sealed products excluded). Omitted, not 0, if temporarily unavailable. |
| `sealed_count` | integer | Sealed products in the set (booster boxes, displays, decks). Omitted, not 0, if temporarily unavailable. |
| `language` | string \| null | Set language |
| `release_date` | string \| null | ISO date |

### Example Request:

*cURL*

```bash
curl -H "X-API-Key: pk_live_your_key_here" \
     "https://api.pokewallet.io/cfv/sets"
```

### Response Example (first two sets):

*200 OK*

```json
{
  "success": true,
  "data": [
    {
      "name": "A Brush with the Legends",
      "set_code": "d-bt02",
      "set_id": "-92",
      "card_count": 212,
      "sealed_count": 0,
      "language": "eng",
      "release_date": null
    },
    {
      "name": "Absolute Judgment",
      "set_code": "g-bt08",
      "set_id": "-72",
      "card_count": 143,
      "sealed_count": 0,
      "language": "eng",
      "release_date": null
    }
  ],
  "total": 420
}
```

### GET /cfv/sets/:set

Cards of one set, paginated. Prices are empty in set listings, as on the Pokémon API: use `/cfv/cards/:id` for prices.

### Parameters:

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `set` | string | Required | `set_id` (recommended, always unique) or `set_code` (exact match, case-insensitive) |
| `page` | number | Optional | Page number (default: 1) |
| `limit` | number | Optional | Results per page (default: 50, max: 200) |

### Example Request:

*cURL*

```bash
curl -H "X-API-Key: pk_live_your_key_here" \
     "https://api.pokewallet.io/cfv/sets/24738?limit=1"
```

### Response Example:

*200 OK*

```json
{
  "success": true,
  "set": {
    "name": "DZ-BT15: Strike of Illusionary Shadows",
    "set_code": "DZ-BT15",
    "set_id": "24738",
    "total_cards": 222,
    "language": "eng",
    "release_date": "2026-08-28T00:00:00"
  },
  "cards": [
    {
      "id": "cfv_5277c3b17c4cf57bd911b31886d90bb88bce908dfc85294cfb5077d3963160c955b5c1fd5b7e0706a0da0eedfdbc32c08e3970df0c45",
      "card_info": {
        "name": "Hellfire Dragon Emperor, Wirbel Kenig",
        "clean_name": "Hellfire Dragon Emperor Wirbel Kenig",
        "set_name": "DZ-BT15: Strike of Illusionary Shadows",
        "set_code": "DZ-BT15",
        "set_id": "24738",
        "card_number": "DZ-BT15/001EN",
        "rarity": "RRR",
        "card_type": null,
        "product_type": "card",
        "variants": [
          "Normal"
        ],
        "card_text": "[CONT]:During this fight, this card is also regarded as \"Hellfire Dragon, Wirbel Dragon\", and can only be ridden from a grade 3 with \"Wirbel\" in its card name.<br> [CONT](VC):The maximum energy you may have in the [CONT] ability of the \"Energy Generator\" in your crest zone gets +5.<br> [AUTO]:When this unit is placed on (VC), [Energy-Charge 3]. Choose a card with original card name \"Hellfire Dragon, Wirbel Dragon\" from your soul or drop, and until end of this fight, this unit gets all of the original abilities of that card.<br> [ACT](VC)[1/Turn]:[COST][Energy-Blast 10], retire all of your opponent's rear-guards, and perform one of the following according to the number of cards in your opponent's damage zone.<br> Four or less - Choose one of your opponent's vanguard, and deal one damage.<br> Five or more - This unit gets [Power]+10000 until end of turn.",
        "race": "Flame Dragon",
        "unit": "Normal Unit",
        "grade": "3",
        "power": "13000",
        "nation": "Dragon Empire",
        "shield": "0",
        "critical": "1",
        "flavor_text": "Swirl and break, hellfire of decimation! Burning Perdition!",
        "skill_icon": "Twin Drive!!",
        "imaginary_gift": "Persona Ride"
      },
      "images": {
        "languages": [
          "en"
        ]
      },
      "tcgplayer": {
        "prices": [],
        "url": "https://www.tcgplayer.com/product/713541/cardfight-vanguard-dz-bt15-strike-of-illusionary-shadows-hellfire-dragon-emperor-wirbel-kenig"
      },
      "cardmarket": {
        "product_name": "Hellfire Dragon Emperor, Wirbel Kenig [D-Format] (V.1 - Triple Rare)",
        "prices": [],
        "product_url": "https://www.cardmarket.com/en/Vanguard/Products/Singles/Strike-of-Illusionary-Shadows/Hellfire-Dragon-Emperor-Wirbel-Kenig-D-Format-V1-Triple-Rare"
      }
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 1,
    "total": 222,
    "total_pages": 222
  }
}
```

### Disambiguation Response:

When a `set_code` is shared by several sets, the API returns the candidates instead of a set (status 200, as on the Pokémon API). Call again with the `set_id` you need.

*200 OK — /cfv/sets/BT01*

```json
{
  "disambiguation": true,
  "message": "Multiple sets found for set_code 'BT01'. Use set_id to specify which one.",
  "matches": [
    {
      "set_id": "-3",
      "set_code": "bt01",
      "name": "Descent of the King of Knights",
      "language": "eng",
      "release_date": null
    },
    {
      "set_id": "1115",
      "set_code": "BT01",
      "name": "BT01: Descent of the King of Knights",
      "language": "eng",
      "release_date": "2011-12-10T00:00:00"
    }
  ],
  "total": 2
}
```

### GET /cfv/cards/:id

Full card with prices. `id` is a `cfv_…` ID or a 64-character CardMarket ID.

### Example Request:

*cURL*

```bash
curl -H "X-API-Key: pk_live_your_key_here" \
     "https://api.pokewallet.io/cfv/cards/cfv_5277c3b17c4cf57bd911b31886d90bb88bce908dfc85294cfb5077d3963160c955b5c1fd5b7e0706a0da0eedfdbc32c08e3970df0c45"
```

### Response Example:

*200 OK*

```json
{
  "id": "cfv_5277c3b17c4cf57bd911b31886d90bb88bce908dfc85294cfb5077d3963160c955b5c1fd5b7e0706a0da0eedfdbc32c08e3970df0c45",
  "card_info": {
    "name": "Hellfire Dragon Emperor, Wirbel Kenig",
    "clean_name": "Hellfire Dragon Emperor Wirbel Kenig",
    "set_name": "DZ-BT15: Strike of Illusionary Shadows",
    "set_code": "DZ-BT15",
    "set_id": "24738",
    "card_number": "DZ-BT15/001EN",
    "rarity": "RRR",
    "card_type": null,
    "product_type": "card",
    "variants": [
      "Normal"
    ],
    "card_text": "[CONT]:During this fight, this card is also regarded as \"Hellfire Dragon, Wirbel Dragon\", and can only be ridden from a grade 3 with \"Wirbel\" in its card name.<br> [CONT](VC):The maximum energy you may have in the [CONT] ability of the \"Energy Generator\" in your crest zone gets +5.<br> [AUTO]:When this unit is placed on (VC), [Energy-Charge 3]. Choose a card with original card name \"Hellfire Dragon, Wirbel Dragon\" from your soul or drop, and until end of this fight, this unit gets all of the original abilities of that card.<br> [ACT](VC)[1/Turn]:[COST][Energy-Blast 10], retire all of your opponent's rear-guards, and perform one of the following according to the number of cards in your opponent's damage zone.<br> Four or less - Choose one of your opponent's vanguard, and deal one damage.<br> Five or more - This unit gets [Power]+10000 until end of turn.",
    "race": "Flame Dragon",
    "unit": "Normal Unit",
    "grade": "3",
    "power": "13000",
    "nation": "Dragon Empire",
    "shield": "0",
    "critical": "1",
    "flavor_text": "Swirl and break, hellfire of decimation! Burning Perdition!",
    "skill_icon": "Twin Drive!!",
    "imaginary_gift": "Persona Ride"
  },
  "images": {
    "languages": [
      "en"
    ]
  },
  "tcgplayer": {
    "prices": [
      {
        "low_price": 0.25,
        "mid_price": 0.5,
        "high_price": 3,
        "market_price": 0.3,
        "direct_low_price": null,
        "updated_at": "2026-09-30T20:40:04.764268+00:00",
        "sub_type_name": "Normal"
      }
    ],
    "url": "https://www.tcgplayer.com/product/713541/cardfight-vanguard-dz-bt15-strike-of-illusionary-shadows-hellfire-dragon-emperor-wirbel-kenig"
  },
  "cardmarket": {
    "product_name": "Hellfire Dragon Emperor, Wirbel Kenig [D-Format] (V.1 - Triple Rare)",
    "prices": [
      {
        "variant_type": "normal",
        "avg": 0.64,
        "low": 0.44,
        "trend": 0.96,
        "avg1": 0.5,
        "avg7": 0.59,
        "avg30": 0.59,
        "updated_at": "2026-09-30T14:49:56.324042+00:00"
      }
    ],
    "product_url": "https://www.cardmarket.com/en/Vanguard/Products/Singles/Strike-of-Illusionary-Shadows/Hellfire-Dragon-Emperor-Wirbel-Kenig-D-Format-V1-Triple-Rare"
  }
}
```

> One entry in `tcgplayer.prices` per finish, identified by `sub_type_name`, and one entry in `cardmarket.prices` per finish, identified by `variant_type` (`normal`, `foil`). `updated_at` is when that price last changed, not when it was last checked. A CardMarket-only card has `tcgplayer: null` and empty `variants`.

### GET /cfv/search

Search cards by name or card number. Results have the same shape as `/cfv/cards/:id`, prices included.

### Parameters:

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `q` | string | Required | 2–100 characters. Card name (all words, any order) or card number |
| `page` | number | Optional | Page number (default: 1) |
| `limit` | number | Optional | Results per page (default: 20, max: 100) |

### Example Request:

*cURL*

```bash
curl -H "X-API-Key: pk_live_your_key_here" \
     "https://api.pokewallet.io/cfv/search?q=Hellfire&limit=2"
```

### Response Example (`results` omitted):

*200 OK*

```json
{
  "query": "Hellfire",
  "results": [
    {
      "id": "f7b35556326fa8eb395a89c477ddb9d66841b65e7aa4b5ddd693e48c9e52a6e5",
      "card_info": {
        "name": "Hellfire Seal Dragon, Blockade Inferno",
        "clean_name": "Hellfire Seal Dragon, Blockade Inferno",
        "set_name": "Seal Dragons Unleashed",
        "set_code": "bt11",
        "set_id": "-61",
        "card_number": "004",
        "rarity": null,
        "card_type": null,
        "product_type": "card",
        "variants": [],
        "card_text": null
      },
      "images": {
        "languages": [
          "en"
        ]
      },
      "tcgplayer": null,
      "cardmarket": {
        "product_name": "Hellfire Seal Dragon, Blockade Inferno",
        "prices": [
          {
            "variant_type": "normal",
            "avg": 2,
            "low": 2.61,
            "trend": 1.74,
            "avg1": 2,
            "avg7": 2,
            "avg30": 2.4,
            "updated_at": "2026-09-30T14:49:55.41944+00:00"
          }
        ]
      }
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 2,
    "total": 20,
    "total_pages": 10
  },
  "metadata": {
    "total_count": 20,
    "tcg": 15,
    "cardmarket": 16,
    "tcg_only": 4,
    "cardmarket_only": 5,
    "both_sources": 11,
    "truncated": false
  }
}
```

> `metadata.truncated: true` means more cards match than the search returns: add words to narrow it down. Words match anywhere in the name; a query shorter than 3 characters matches names and card numbers that **start** with it (`27` finds `27`, `027/264`, `0027`). Quotes are ignored; `*`, `%` and `_` are plain text, not wildcards.

### GET /cfv/images/:id

The card image, one resolution (`X-Image-Size: unsized`): the `size` parameter of the Pokémon image endpoint does not apply. Cached for 24 hours.

### Example Request:

*cURL*

```bash
curl -H "X-API-Key: pk_live_your_key_here" \
     "https://api.pokewallet.io/cfv/images/cfv_5277c3b17c4cf57bd911b31886d90bb88bce908dfc85294cfb5077d3963160c955b5c1fd5b7e0706a0da0eedfdbc32c08e3970df0c45" \
     --output card
```

## Ultra endpoints

Available on Ultra and Business plans, or during the 7-day trial. The Pro plan covers Pokémon and One Piece only: with Pro these endpoints answer 403 with an `upgrade_url`. Ultra includes everything in Pro. Same parameters and response format as the Pokémon endpoints of the same name.

| Endpoint | What it returns |
| --- | --- |
| `GET /cfv/prices/:set` | TCGPlayer and CardMarket prices for every card of a set, one row per finish. `?source=tcg\|cm` |
| `GET /cfv/sets/:set/statistics` | Price statistics and rarity breakdown. Sealed products excluded. `?variant=normal\|foil` |
| `GET /cfv/sets/:set/completion-value` | Cost of one copy of every card in the set. Sealed products excluded. |
| `GET /cfv/sets/trending` | Sets whose CardMarket prices moved most: 7-day average against 30-day average, cards from 1 € up. `?period=7d` `?limit=` |
| `GET /cfv/analytics/top-cards` | Most expensive cards, or fastest-growing on CardMarket (7-day average against 30-day average, cards from 1 € up; `current_price` is the 7-day average). `?metric=price\|growth` `?source=tcg\|cm` `?limit=` |

> Price history is a Pokémon feature: for this game there is no `/cards/:id/price-history`. Current prices are in the card detail and in `/cfv/prices/:set`; `metric=growth` works with `source=cm` only, and trending supports `period=7d` only.

### GET /cfv/prices/:set (first rows)

*200 OK*

```json
{
  "success": true,
  "source": "both",
  "set_code": "DZ-BT15",
  "set_id": "24738",
  "data": [
    {
      "id": "cfv_5277c3b17c4cf57bd911b31886d90bb88bce908dfc85294cfb5077d3963160c955b5c1fd5b7e0706a0da0eedfdbc32c08e3970df0c45",
      "card_number": "DZ-BT15/001EN",
      "name": "Hellfire Dragon Emperor, Wirbel Kenig",
      "product_type": "card",
      "variant": "Normal",
      "tcgplayer": {
        "low_price": 0.25,
        "mid_price": 0.5,
        "high_price": 3,
        "market_price": 0.3,
        "direct_low_price": null,
        "updated_at": "2026-09-30T20:40:04.764268+00:00"
      },
      "cardmarket": {
        "avg": 0.64,
        "low": 0.44,
        "trend": 0.96,
        "avg1": 0.5,
        "avg7": 0.59,
        "avg30": 0.59,
        "updated_at": "2026-09-30T14:49:56.324042+00:00"
      }
    },
    {
      "id": "cfv_a7616e7cda480cd8ac92174e2e8c0a613af43ff7d620c414734a285a9db1e679894fa3a78f92cd199805f967f245bf42a5c17772d970",
      "card_number": "DZ-BT15/002EN",
      "name": "Brawler, Compete Dragon",
      "product_type": "card",
      "variant": "Normal",
      "tcgplayer": {
        "low_price": 0.7,
        "mid_price": 1.97,
        "high_price": 4.99,
        "market_price": 0.93,
        "direct_low_price": null,
        "updated_at": "2026-09-30T20:40:04.764268+00:00"
      },
      "cardmarket": {
        "avg": 2.29,
        "low": 0.5,
        "trend": 0.96,
        "avg1": 2,
        "avg7": 2.45,
        "avg30": 2.45,
        "updated_at": "2026-09-30T14:49:56.324042+00:00"
      }
    },
    {
      "id": "cfv_aca4bf9a636c4332a38d5b416baaccb3bf606ef96244f3b8b8a5d1c9a4c6b643fe0203b2bbdaab3bb593cc9c08f8e95768dcf7512046",
      "card_number": "DZ-BT15/003EN",
      "name": "Dragritter, Najuura",
      "product_type": "card",
      "variant": "Normal",
      "tcgplayer": {
        "low_price": 0.49,
        "mid_price": 0.77,
        "high_price": 4.79,
        "market_price": 0.68,
        "direct_low_price": null,
        "updated_at": "2026-09-30T20:40:04.764268+00:00"
      },
      "cardmarket": {
        "avg": 1.25,
        "low": 0.75,
        "trend": 1.11,
        "avg1": 0.96,
        "avg7": 1.23,
        "avg30": 1.23,
        "updated_at": "2026-09-30T14:49:56.324042+00:00"
      }
    }
  ],
  "total": 222
}
```

### GET /cfv/sets/:set/statistics

*200 OK*

```json
{
  "set_info": {
    "name": "DZ-BT15: Strike of Illusionary Shadows",
    "set_code": "DZ-BT15",
    "set_id": "24738",
    "total_cards": 219
  },
  "price_statistics": {
    "tcgplayer": {
      "avg_market_price": "15.53",
      "min_price": 0.06,
      "max_price": 850.08,
      "most_expensive": {
        "id": "cfv_0814de74c6a01a77565e4a622473ac2d67927e6d1115c62e8085b7b59b4096bedda04d638a68ec3ad35e422fa0bbc80982c2aa26bad9c5bc",
        "name": "Star-vader, Chaos Breaker Dragon (SEC)",
        "price": 850.08
      }
    },
    "cardmarket": {
      "avg_price": "6.97",
      "min_price": 0.05,
      "max_price": 167.39,
      "most_expensive": {
        "id": "cfv_7da2f67d7e4353ea2fde7e1907e3c5891fb3604093f8df28329e33665f3c5a683459801dcc13cadb93694de11b15341b49571dd852f442c8d06709",
        "name": "Blaster Dark (15thSP)",
        "price": 167.39
      },
      "trend_direction": "stable"
    }
  },
  "rarity_breakdown": {
    "RRR": 19,
    "15thSP": 4,
    "RR": 21,
    "R": 21,
    "C": 43,
    "Double Frame Rare": 19,
    "Frame Rare": 42,
    "PGS": 1,
    "SEC": 6,
    "SR": 40,
    "T": 2
  }
}
```

### GET /cfv/sets/:set/completion-value

*200 OK*

```json
{
  "set_info": {
    "name": "DZ-BT15: Strike of Illusionary Shadows",
    "set_code": "DZ-BT15",
    "set_id": "24738",
    "total_cards": 219
  },
  "completion_cost": {
    "tcgplayer": {
      "currency": "USD",
      "low_estimate": 3560.52,
      "market_estimate": 4334.74,
      "high_estimate": 6282.97
    },
    "cardmarket": {
      "currency": "EUR",
      "low_estimate": 3511.59,
      "avg_estimate": 3795.65,
      "trend_estimate": 1265.7
    },
    "note": "One copy per card (base finish). TCGPlayer prices in USD, CardMarket prices in EUR. Cross-currency comparison not available."
  },
  "breakdown_by_rarity": {
    "RRR": {
      "count": 19,
      "total_cost": 75.46
    },
    "15thSP": {
      "count": 4,
      "total_cost": 446.73
    },
    "RR": {
      "count": 21,
      "total_cost": 16.6
    },
    "R": {
      "count": 21,
      "total_cost": 4.59
    },
    "C": {
      "count": 43,
      "total_cost": 4.98
    },
    "Double Frame Rare": {
      "count": 19,
      "total_cost": 738.11
    },
    "Frame Rare": {
      "count": 42,
      "total_cost": 135.17
    },
    "PGS": {
      "count": 1,
      "total_cost": 0
    },
    "SEC": {
      "count": 6,
      "total_cost": 1628.18
    },
    "SR": {
      "count": 40,
      "total_cost": 289.26
    },
    "T": {
      "count": 2,
      "total_cost": 0.73
    }
  }
}
```

### GET /cfv/analytics/top-cards

*200 OK*

```json
{
  "metric": "price",
  "source": "tcg",
  "top_cards": [
    {
      "id": "cfv_13dafb189e5051ab2f3da8ddd310a890f1ff87b0e9be03bacdb4541dc751bd20918302ce864ec3a94d38bfa14bac8551fcaf0916ecc7e2b7",
      "card_info": {
        "name": "Blaster Blade",
        "clean_name": "Blaster Blade",
        "set_name": "DZ-SS15: The Legendary Vanguards",
        "set_code": "DZ-SS15",
        "set_id": "24592",
        "card_number": "DZ-SS15/PGS01EN",
        "rarity": "PGS",
        "card_type": null,
        "product_type": "card",
        "variants": [
          "Normal"
        ],
        "card_text": "[AUTO]:[Counter-Blast 2] When this unit is placed on (VC), you may pay the cost. If you do, choose an opponent's rear-guard, and retire it.<br>\n[AUTO]:[Counter-Blast 2] When this unit is placed on (RC), if you have a <<Royal Paladin>> vanguard, you may pay the cost. If you do, choose an opponent's grade 2 or greater rear-guard, and retire it.",
        "clan": "Royal Paladin",
        "race": "Human",
        "grade": "2",
        "power": "9000",
        "nation": "United Sanctuary",
        "shield": "0",
        "critical": "1",
        "flavor_text": "Shine forth, brave sword and defender of the weak! Blaster Blade!"
      },
      "current_price": 1088.99
    },
    {
      "id": "cfv_c752237f2cc982beaf4e125558c64604de8afdef0bfd5efd05fe3921bf6a5cf673de789ad26e0236c4d8727f47b488cb7126fe95453b",
      "card_info": {
        "name": "Elementaria Sanctitude",
        "clean_name": "Elementaria Sanctitude",
        "set_name": "Bushiroad Event Cards",
        "set_code": "EVENT",
        "set_id": "2601",
        "card_number": "BCS2526/VGS03a",
        "rarity": "PR",
        "card_type": null,
        "product_type": "card",
        "variants": [
          "Normal"
        ],
        "card_text": "[CONT]:Sentinel <em>(You may only have up to four cards with \"[CONT]:<strong>Sentinel</strong>\" in a deck)</em><br>[CONT]:You may only have one \"Elementaria Sanctitude\" in a deck.<br>If your opponent's vanguard has <span style=\"color:red\">\"Triple Drive\"</span>, this ability can be played without paying the cost. <em>(Your vanguard must be grade 3 or less)</em><br>Play this with [COST][discard a card from hand] if your vanguard is grade 3 or less!<br>Choose one of your units, and it cannot be hit until end of that battle. Remove this card.",
        "race": "Abyss Dragon",
        "unit": "Blitz Order",
        "grade": "1",
        "power": "0",
        "shield": "0",
        "critical": "0",
        "flavor_text": "The spirits answered the voices yearning for tomorrow with a brilliant shield.",
        "skill_icon": "0"
      },
      "current_price": 1000
    },
    {
      "id": "cfv_cb63b98d9b255790a2718b3f0ce13f4d0c3dbe4a01c743b71ac06f1a784f85e7fe822b6fa65d2b3c90fdfa49fc29649f60a02791abec06d4",
      "card_info": {
        "name": "One Who Bears Resolve and Sin, Liael=Animus (SEC)",
        "clean_name": "One Who Bears Resolve and Sin Liael=Animus SEC",
        "set_name": "DZ-BT13: Parallactic Clash",
        "set_code": "DZ-BT13",
        "set_id": "24615",
        "card_number": "DZ-BT13/SEC03EN",
        "rarity": "SEC",
        "card_type": null,
        "product_type": "card",
        "variants": [
          "Normal"
        ],
        "card_text": "[CONT]:This card can only be ridden from a grade 3 card with \"Odium\" or \"Amorta\" in its card name, and the normal ride from those cards also becomes a persona ride.<br> [AUTO]:When this unit is placed on (VC), choose a grade 3 card with \"Odium\" or \"Amorta\" in its card name from your soul or drop, and this unit gets the card name and all of the abilities of the chosen card until end of this fight. Look at the top five cards of your deck, choose up to one card from among them, and bind it or put it face up as a regress gauge. If you bound a card, choose a card from your bind zone, and put it into drop. If you put a card as regress gauge, choose a card from your regress gauge, and discard it. Shuffle the deck.<br> [ACT](VC)[1/Turn]:If you used a [Divine Skill] this fight, [COST][Energy-Blast 3], and this unit gets <span style=\"color:red\">\"[AUTO](VC)[1/Turn]:At the end of the battle this unit attacked, choose one of your rear-guards, and [Stand] it.\"</span> until end of turn.",
        "race": "Angel",
        "unit": "Normal Unit",
        "grade": "3",
        "power": "13000",
        "nation": "Dark States",
        "shield": "0",
        "critical": "1",
        "skill_icon": "Twin Drive!!",
        "imaginary_gift": "Persona Ride"
      },
      "current_price": 997.97
    }
  ]
}
```

### GET /cfv/analytics/top-cards?metric=growth&source=cm

*200 OK*

```json
{
  "metric": "growth",
  "source": "cm",
  "top_cards": [
    {
      "id": "5e989b44483fdcff9c46d197b14b4dda3842fc39cf9b6b0227f84323a0272803",
      "card_info": {
        "name": "Tragic Claw",
        "clean_name": "Tragic Claw",
        "set_name": "Glorious Bravery of Radiant Sword",
        "set_code": "g-bt07",
        "set_id": "-109",
        "card_number": "087",
        "rarity": null,
        "card_type": null,
        "product_type": "card",
        "variants": [],
        "card_text": null
      },
      "current_price": 5.06,
      "change_7d": "+360.0%"
    },
    {
      "id": "eaa46b6bb83bad4ee89cad12c2687b5785b310db582cca148ba7e4d0d289c198",
      "card_info": {
        "name": "Imaginary Gift Accel II",
        "clean_name": "Imaginary Gift Accel II",
        "set_name": null,
        "set_code": null,
        "set_id": null,
        "card_number": "011",
        "rarity": null,
        "card_type": null,
        "product_type": "card",
        "variants": [],
        "card_text": null
      },
      "current_price": 5.1,
      "change_7d": "+328.6%"
    },
    {
      "id": "cfv_cb2c8d93a9ae9441075023aed55f8f9235c49e611c59241a19e9b89de0220096f375001fde8e459d4e74f9ab5ac910686b67ba7b763edc",
      "card_info": {
        "name": "Zypsophila Fairy, Asher",
        "clean_name": "Zypsophila Fairy Asher",
        "set_name": "DZ-SS01: Festival Booster 2024",
        "set_code": "DZ-SS01",
        "set_id": "23438",
        "card_number": "DZ-SS01/Re28EN",
        "rarity": "RE",
        "card_type": null,
        "product_type": "card",
        "variants": [
          "Normal"
        ],
        "card_text": "[AUTO]:When this unit is placed on (GC), if your opponent's attacking unit has attacked two or more times this turn, this unit gets [Shield] +15000 until end of that battle. (Active if this battle is its second attack this turn or more)",
        "race": "Sylph",
        "unit": "Trigger",
        "grade": "0",
        "power": "4000",
        "nation": "Stoicheia",
        "shield": "10000",
        "trigger": "Heal",
        "critical": "1",
        "flavor_text": "A phantasmal garden where happiness blooms, that evildoers cannot reach.",
        "skill_icon": "Boost"
      },
      "current_price": 4.29,
      "change_7d": "+316.5%"
    }
  ]
}
```

### GET /cfv/sets/trending

*200 OK*

```json
{
  "period": "7d",
  "trending_sets": [
    {
      "set_name": "Premium Collection 2020",
      "set_code": "v-ss05",
      "set_id": "-97",
      "card_count": 65,
      "cardmarket": {
        "price_change": "+53.4%",
        "avg_price_current": 12.29,
        "avg_price_previous": 8.01,
        "top_movers": [
          {
            "name": "Heat Wave Beast, Geomaglass",
            "change": "+210.8%"
          },
          {
            "name": "Heat Wave Beast, Geomaglass",
            "change": "+195.4%"
          },
          {
            "name": "Conquering Supreme Dragon, Stunverse Dragon",
            "change": "+193.2%"
          }
        ]
      },
      "tcgplayer": null
    },
    {
      "set_name": "Trial Deck: Blue Cavalry of the Divine Marine Spirits",
      "set_code": "g-td04",
      "set_id": "-37",
      "card_count": 5,
      "cardmarket": {
        "price_change": "+48.6%",
        "avg_price_current": 5.89,
        "avg_price_previous": 3.97,
        "top_movers": [
          {
            "name": "Officer Cadet, Andrey",
            "change": "+67.2%"
          },
          {
            "name": "Marine General of the Heavenly Scales, Tidal Bore Dragon",
            "change": "+62.6%"
          },
          {
            "name": "One Who Surpasses the Storm, Thavas",
            "change": "+47.9%"
          }
        ]
      },
      "tcgplayer": null
    }
  ]
}
```

## Data Notes

> **Prices.** TCGPlayer and CardMarket prices are refreshed daily. CardMarket has one entry per finish (`normal`, `foil`); a finish CardMarket does not price is left out, so `cardmarket.prices` can be empty. `updated_at` is when that price last changed, not when it was last checked: a stable price keeps an older date.

> **`product_type`.** Cards and sealed products are told apart using TCGPlayer data, then CardTrader. Tokens, emblems and oversized cards count as `"card"`. For the very few products whose type cannot be determined, `product_type` is `null`, not `"card"`.

> **CardMarket-only sets.** Sets with a negative `set_id` exist only on CardMarket: their cards have 64-character IDs and `tcgplayer: null`.

> **Missing links.** `cardmarket.product_url` is absent for some cards; the card data is still valid. Sets with no cards are not listed: they appear as soon as cards are added.

## Error Handling

The API uses standard HTTP status codes to indicate success or failure:

- **200 OK** — Request successful
- **400 Bad Request** — Invalid parameters or malformed request
- **401 Unauthorized** — Missing or invalid API key
- **403 Forbidden** — Paid endpoint without the required plan or an active trial: Pro for Pokémon and One Piece, Ultra for the other games. The body includes `redeem_url` (trial not activated yet) or `upgrade_url` (trial expired).
- **404 Not Found** — Card, set or image does not exist
- **405 Method Not Allowed** — The API only accepts `GET` requests.
- **409 Conflict** — `/sets/:setCode/image`: the set code matches several sets. Retry with `?language=` or the numeric `set_id`.
- **429 Too Many Requests** — Rate limit exceeded
- **500 Internal Server Error** — Something went wrong on our side
- **502 Bad Gateway** — An image could not be fetched from its source. Temporary: retry later.

### Errors on game endpoints (`/mtg`, `/ygo`, …)

| Status | `error` | When |
| --- | --- | --- |
| 400 | `Invalid or corrupted card ID` | ID is neither `<game>_…` nor 64 hex, or belongs to another game |
| 400 | `Query parameter required` | `q` missing, shorter than 2 or longer than 100 characters |
| 400 | `Search query rejected` | `q` contains SQL-like text: rephrase it |
| 400 | `Invalid or corrupted card ID` + `did_you_mean` | The ID belongs to another game (e.g. a `lor_…` ID on `/mtg/cards`): `did_you_mean` has the right path |
| 404 | `Set not found` + `suggestions` | No set with that `set_id`/`set_code`. If you wrote the set *name*, `suggestions` lists the matching sets with their `set_id` |
| 404 | `Card not found`, `Image not found` | Card or image does not exist |
| 502 | `Image not available` | Image source temporarily unreachable |
| 503 | `Search temporarily unavailable` | The search took too long under load. Retry after the seconds in the `Retry-After` header, or use more specific words |

### Error Response Format

Every error has `error` (short, stable) and `message` (what to fix). When the API can tell what you meant, `did_you_mean` contains the corrected path:

```
// GET /magic/sets
  {
    "error": "Endpoint not found",
    "message": "Did you mean /mtg/sets?",
    "did_you_mean": "/mtg/sets",
    "docs": "https://pokewallet.io/api-docs"
  }
```

Authentication errors (401) also include an `example` request. A trailing slash is ignored: `/sets/` and `/sets` are the same endpoint.

## Best Practices

### Use Environment Variables

Never hardcode API keys. Always use environment variables or secure secret management.

### Handle Rate Limits Gracefully

Implement exponential backoff when you receive a 429 response. Check rate limit headers to avoid hitting limits.

### Cache Results

Cache API responses when appropriate to reduce requests and improve performance.

### Use Search Endpoint

Always use `/search` for the most complete and accurate data.

### Monitor Your Usage

Check your dashboard regularly to track API usage and avoid unexpected rate limit hits.

### Error Handling

Always implement proper error handling for network failures, rate limits, and invalid responses.

> **Security Reminder:** Keep your API keys secure. If you suspect a key has been compromised, revoke it immediately from your dashboard and generate a new one.

## Cardfight!! Vanguard API FAQ

### Is there a free Cardfight!! Vanguard price API?

Yes. The free tier (100 requests/hour, 1,000/day) opens the Cardfight!! Vanguard sets, cards, search and images endpoints, with TCGPlayer and CardMarket prices refreshed daily. Prices by set, set statistics, completion value, trending sets and top cards need the Ultra plan (€29/month) or its 7-day trial.

### Does the Cardfight!! Vanguard API include CardMarket prices?

Yes. Cards return TCGPlayer (USD) prices for every finish and CardMarket (EUR) prices for normal and foil, where that market lists the card. Cards sold only on CardMarket have a 64-character hex id and no TCGPlayer price.

### How do I find the id of a Cardfight!! Vanguard card?

Search with /cfv/search?q=… or list a set with /cfv/sets/:set. Each card has an id (cfv_ + hex for cards on TCGPlayer) to pass to /cfv/cards/:id and /cfv/images/:id.

### Can I use the same API key for other games?

Yes. One PokéWallet API key works for all 12 games, Pokémon and One Piece included, and the rate limits are shared across them.

## Need Help?

If you have questions or run into issues, we're here to help!

[Go to Dashboard](https://pokewallet.io/dashboard)

[Contact Support](mailto:hello@pokewallet.io)

[Join Discord](https://discord.gg/znDxEjmDVP)
