# TableArchive documentation Source: https://tablearchive.dev/llms-full.txt. The short index is at https://tablearchive.dev/llms.txt. # Introduction > What TableArchive is, what it holds, and how to use it. TableArchive is a database of board games, card games, and tabletop RPGs with a JSON API. It holds **facts**: players, playtime, age, year, credits, editions and expansions, barcodes, IDs in other systems, and photos. It holds no ratings, reviews, or prices. It is small on purpose. Use it to put game facts in your collection tracker, barcode scanner, Discord bot, or the app you built with an AI assistant. When you need more, every item links to [BoardGameGeek](https://boardgamegeek.com) and Wikidata where we have the IDs. ## What you get - `GET /v1/items` to search and filter, `GET /v1/items/{slug}` for everything about one item. No key needed to read. - Lookup by barcode, Wikidata QID, or BGG ID. - Photos resized to 320 and 1024 px as WebP. - The source of every field, and a history of every change. - An [OpenAPI document](https://tablearchive.dev/openapi.json), [llms.txt](https://tablearchive.dev/llms.txt), and every page here as Markdown. ## How it is built Our own work is public domain ([CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/)), offered with no warranty. Facts and photos that come from publishers, Wikidata, and other sources keep their sources' licenses, and the API returns those with the data. See [Sources and licenses](https://tablearchive.dev/docs/sources-licenses). > Start with the [Quick start](https://tablearchive.dev/docs/quick-start). It takes two minutes. # Quick start > Make your first request in two minutes: get a key, search, fetch one item, and filter. ## Get a key Reading needs no key, so the search below works as it is. For photos, contributing, and a higher rate limit: log in, open **Account → API keys**, and create one. Keys start with `bgdb_` and are shown once. ## Search ```bash curl "https://tablearchive.dev/v1/items?q=wingspan" \ -H "Authorization: Bearer $TA_KEY" ``` ```javascript const res = await fetch('https://tablearchive.dev/v1/items?q=wingspan', { headers: { Authorization: `Bearer ${process.env.TA_KEY}` }, }) const { total, items } = await res.json() ``` ```python import os, requests res = requests.get( "https://tablearchive.dev/v1/items", params={"q": "wingspan"}, headers={"Authorization": f"Bearer {os.environ['TA_KEY']}"}, ) items = res.json()["items"] ``` ```json { "total": 3, "items": [{ "id": "9b2e…", "kind": "board_game", "name": "Wingspan", "slug": "wingspan", "data": { "year_published": 2019, "min_players": 1, "max_players": 5, "min_age": 10 }, "image": { "thumb_url": "https://tablearchive.dev/v1/images/…/file?size=thumb&…" }, "updated_at": "2026-10-04T18:21:07.000Z" }] } ``` ## Fetch one item ```bash curl "https://tablearchive.dev/v1/items/wingspan" -H "Authorization: Bearer $TA_KEY" ``` The response adds `credits`, `terms`, `relations`, `images`, `external_ids`, and `provenance` (which source set each field). See [Items](https://tablearchive.dev/docs/items). ## Filter Combine any of these on `GET /v1/items`: | Parameter | Example | What it does | |---|---|---| | `q` | `catan` | Search names and descriptions | | `kind` | `board_game` | One kind; see [Items](https://tablearchive.dev/docs/items#kinds) | | `players` | `2` | Playable with exactly this many | | `max_playtime` | `45` | At most this many minutes | | `age` | `8` | Suitable for a player this old | | `year` | `2019` | Published that year | | `external_id` | `029877030712` | A barcode, Wikidata QID, or BGG ID | Full list in [Search and filters](https://tablearchive.dev/docs/search). > Building with an AI assistant? Give it [https://tablearchive.dev/llms.txt](https://tablearchive.dev/llms.txt). It describes this API in plain text. # Authentication > API keys, the Authorization header, and what a key can do. Reading the catalog needs no key. A key gets you photos, contributing, and a higher rate limit: ```http GET /v1/items?q=catan Authorization: Bearer bgdb_… ``` | | Without a key | With a key | |---|---|---| | Items, search, lookup, credits, terms, history | yes | yes | | Photo links | first page of a list only | yes | | Submissions and uploads | no | yes | | Requests per minute | 120 per address | 600 (user) or 6,000 (bot) | ## Keys Make keys in the web app under **Account → API keys**. Each key is shown once, has a label so you know what it is for, and can be revoked there. A key never expires on its own. Treat a key like a password: keep it in an environment variable or a secret store, not in client-side code. If a key leaks, revoke it and make another. ## What a key can do | With a key you can | Not with a key | |---|---| | Read everything | Create accounts, log in, or reset passwords | | Submit new items and changes (see [Submissions](https://tablearchive.dev/docs/submissions)) | Review or approve anything | | Upload photos | Make or revoke keys | | See your own submissions and `GET /v1/me` | Admin functions | Accounts and administration happen only in the web app. An admin's API key acts like any user's key: its submissions wait for review. ## Roles | Role | Submissions | Rate limit | |---|---|---| | user | Reviewed by an admin before publishing | 600 requests/minute | | bot | Published at once, unless flagged as a possible duplicate | 6,000 requests/minute | Bot keys are given to scrapers and trusted integrations. Ask if you need one. # Rate limits and errors > Request limits, the 429 response, and the shape of every error. ## Limits | Who | Requests per minute | Contributions per hour | |---|---|---| | No key | 120, per address | none | | User key | 600 | 200 submissions and photo uploads | | Bot or admin key | 6,000 | unlimited | Over the limit, the API answers `429` with a `Retry-After` header in seconds. Wait that long and try again. ## Errors Every error is JSON with a plain-English `error`: ```json { "error": "Item not found" } ``` Validation errors add `details`, one entry per problem: ```json { "error": "Invalid request", "details": [{ "path": "/payload/data/min_players", "message": "Invalid input: expected number" }] } ``` | Status | Meaning | |---|---| | `400` | The request is malformed; see `details` | | `401` | The request needs a key: contributing, or a photo without its signed link | | `403` | The key may not do this (for example, admin functions) | | `404` | Nothing at that id or slug. Merged items redirect to the survivor instead | | `409` | A conflict, such as an email that is already taken | | `413` | A photo over the size limit | | `429` | Rate limited; see `Retry-After` | ## Be a good client - Cache responses. Items change rarely; `updated_at` tells you when. - Page with `limit` (up to 200) and `offset`. - Poll `GET /v1/events?after=` for changes instead of re-reading everything. See [History](https://tablearchive.dev/docs/history). - Send a `User-Agent` that names your app, so we can reach you if something goes wrong. # Items and kinds > The item record, its known fields, and the kinds of things in the catalog. An **item** is anything in the catalog: a game, an expansion, an RPG book, a card set. `GET /v1/items/{id-or-slug}` returns one in full. ```json { "id": "fc0f21c4-…", "kind": "board_game", "name": "The Settlers of Catan", "slug": "the-settlers-of-catan", "data": { "year_published": 1995, "min_players": 3, "max_players": 4, "min_playtime": 75, "max_playtime": 75, "min_age": 10, "website": "https://www.catan.com/", "short_description": "board game (1995)" }, "provenance": { "year_published": { "source": "wikidata", "url": "https://www.wikidata.org/wiki/Q17271" } }, "credits": [{ "role": "designer", "id": "…", "kind": "person", "name": "Klaus Teuber", "slug": "klaus-teuber" }], "terms": [{ "kind": "mechanic", "name": "Trading", "slug": "trading" }], "relations": [{ "type": "expansion_of", "direction": "incoming", "id": "…", "kind": "expansion", "name": "Seafarers of Catan", "slug": "seafarers-of-catan" }], "images": [{ "id": "…", "kind": "box_front", "url": "…", "medium_url": "…", "thumb_url": "…", "license": "CC BY-SA 3.0", "attribution": "…" }], "image_count": 1, "external_ids": [{ "source": "bgg", "id": "13", "url": "https://boardgamegeek.com/boardgame/13" }], "created_at": "…", "updated_at": "…" } ``` `images` is filled only for requests with a key or a session (see [Photos](https://tablearchive.dev/docs/photos)); `image_count` is always there. Lists show photos on their first page to everyone. Slugs are stable and readable; ids are UUIDs. Either works in the URL. If two items are merged, the old id and slug redirect to the survivor. ## Fields in `data` These are typed and validated. Anything else a source records (box size, component counts, card attributes) is kept as-is, so you may see other keys. | Field | Type | Notes | |---|---|---| | `description` | string | Up to 20,000 characters | | `short_description` | string | One line, e.g. "card game from 1890s Hungary" | | `min_players`, `max_players` | integer | | | `min_playtime`, `max_playtime` | integer | Minutes | | `min_age` | integer | | | `year_published` | integer | | | `alternate_names` | string[] | Other titles and translations | | `website` | URL | | | `language` | string | | `provenance` names the source of each field. See [History and provenance](https://tablearchive.dev/docs/history). ## Kinds | Kind | What it is | |---|---| | `board_game` | A board game | | `card_game` | A card game, including trading card games as a whole | | `card_set` | One set or expansion of a trading card game; left out of listings unless asked for | | `rpg` | A tabletop role-playing game (the core rules) | | `rpg_supplement` | A sourcebook or rules supplement | | `rpg_adventure` | An adventure or campaign | | `miniatures_game` | A miniatures wargame | | `expansion` | An expansion for a game | | `edition` | One edition or printing of a game; left out of listings unless asked for | | `accessory` | Sleeves, inserts, playmats, dice | | `other` | Anything else | `GET /v1/items` leaves out `edition` and `card_set` items unless you ask for that `kind` or search by `external_id`, so a search for a game returns the game, not its forty printings. # Search and filters > Every parameter of GET /v1/items, and how results are sorted. ```http GET /v1/items?q=…&kind=…&players=…&max_playtime=…&age=…&year=…&term=…&entity=…&external_id=…&sort=…&limit=…&offset=… ``` All parameters are optional and combine with AND. | Parameter | Type | What it does | |---|---|---| | `q` | string | Full-text search on names and descriptions, plus a fuzzy match on the name | | `kind` | enum | One [kind](https://tablearchive.dev/docs/items#kinds). Without it, editions and card sets are left out | | `players` | integer | Items playable with exactly this many (`min_players ≤ n ≤ max_players`) | | `max_playtime` | integer | Items that finish within this many minutes | | `age` | integer | Items with `min_age` at or below this | | `year` | integer | `year_published` equals this | | `term` | slug | Items tagged with this mechanic or category, e.g. `deck-building`. See [Credits, terms, relations](https://tablearchive.dev/docs/credits-terms-relations) | | `entity` | UUID | Items credited to this person or organization | | `external_id` | string | Items with this barcode or ID in another system. See [Lookup](https://tablearchive.dev/docs/lookup) | | `external_source` | string | Narrow `external_id` to one source, e.g. `gtin`, `wikidata`, `bgg` | | `sort` | enum | `relevance`, `popular`, `name`, `year`, `updated` | | `limit` | 1–200 | Page size, default 50 | | `offset` | integer | Skip this many | ## Sorting Without `sort`, results are ordered by **relevance** when there is a `q`, and by **popular** otherwise. "Popular" means well-linked, photographed, complete items first; it is a measure of how much we know, not of how good a game is. ## Response ```json { "total": 471, "items": [ … ] } ``` Each item in the list has `id`, `kind`, `name`, `slug`, `data`, `updated_at`, and `image` (the box front's `thumb_url`, `medium_url`, and `url`, or `null`). Fetch `GET /v1/items/{slug}` for the rest. ## Examples Two-player games under 45 minutes, newest first: ```bash curl "https://tablearchive.dev/v1/items?players=2&max_playtime=45&sort=year" -H "Authorization: Bearer $TA_KEY" ``` Everything by one designer: ```bash curl "https://tablearchive.dev/v1/entities?q=uwe+rosenberg" -H "Authorization: Bearer $TA_KEY" curl "https://tablearchive.dev/v1/items?entity=" -H "Authorization: Bearer $TA_KEY" ``` # Lookup by barcode or ID > Find an item from a barcode scan or an ID in another database. Items carry IDs from other systems in `external_ids`. Search by any of them: ```bash curl "https://tablearchive.dev/v1/items?external_id=029877030712" -H "Authorization: Bearer $TA_KEY" ``` ## Barcodes UPC, EAN, GTIN, and ISBN are all accepted and normalized, so a 12-digit UPC matches the 13-digit EAN form of the same code. Barcodes are stored under the source `gtin`. A barcode usually belongs to one edition, so this is the one search that includes `edition` and `card_set` items. ## Other systems | Source | ID looks like | Links to | |---|---|---| | `wikidata` | `Q17271` | wikidata.org | | `bgg` | `13` | boardgamegeek.com | | `rpggeek` | `rpgitem/49826` | rpggeek.com | | `scryfall`, `tcgdex`, `ygoprodeck` | set codes | Card databases | | `shopify:` | product handle | The publisher's shop page | Add `external_source` to search one system only: ```bash curl "https://tablearchive.dev/v1/items?external_id=13&external_source=bgg" -H "Authorization: Bearer $TA_KEY" ``` ## Moving on to BoardGameGeek If your app outgrows TableArchive, the `bgg` ID on each item maps straight to BGG's XML API. We expect that and think it is fine. # Photos > Photo sizes, signed links, and the license on each one. Each item's `images` lists its approved photos, box front first: ```json { "id": "…", "kind": "box_front", "caption": null, "width": 1600, "height": 1600, "mime": "image/jpeg", "url": "https://tablearchive.dev/v1/images/…/file?exp=…&sig=…", "medium_url": "https://tablearchive.dev/v1/images/…/file?size=medium&exp=…&sig=…", "thumb_url": "https://tablearchive.dev/v1/images/…/file?size=thumb&exp=…&sig=…", "source_page_url": "https://commons.wikimedia.org/wiki/File:…", "license": "CC BY-SA 3.0", "attribution": "Matěj Baťha" } ``` ## Sizes | Link | Width | Format | |---|---|---| | `thumb_url` | up to 320 px | WebP | | `medium_url` | up to 1024 px | WebP | | `url` | original | as uploaded | Photos are never enlarged. Use `thumb_url` in lists and `medium_url` on detail pages. ## Who gets them Photos carry other people's licenses, so photo links go to requests with a key or a session. Without one, `image` is filled on the first page of a list only (`offset=0`), later pages get `null`, and an item's `images` is `[]`; `image_count` says how many there are. ## Signed links Photo links carry `exp` and `sig` and work without a key for a few hours, so a plain `` tag can show them. Fetch the item again for fresh ones rather than storing the links. ## What a photo shows `kind` is one of `box_front`, `box_back`, `box_side`, `components`, `detail`, `gameplay`, or `other`. ## Licenses Every photo keeps its own `license` and `attribution`, which you must carry along when you show it. Many come from Wikimedia Commons under Creative Commons licenses; publishers' photos are shown with their permission and may not be reused outside your use of this API. Where `license` is null, treat the photo as all rights reserved. To add a photo, see [Uploading photos](https://tablearchive.dev/docs/uploading-photos). # Credits, terms, relations > People and publishers, mechanics and categories, and how items connect. ## Credits `credits` names the people and organizations behind an item, each with a `role`: `designer`, `artist`, `author`, `developer`, `publisher`. Some sources file designers under `author`. Each credit is an **entity** with its own page: ```bash curl "https://tablearchive.dev/v1/entities?q=teuber" -H "Authorization: Bearer $TA_KEY" curl "https://tablearchive.dev/v1/entities/klaus-teuber" -H "Authorization: Bearer $TA_KEY" ``` `GET /v1/entities` takes `q`, `kind` (`person` or `organization`), `limit`, and `offset`, and returns each entity's `item_count`. `GET /v1/entities/{id-or-slug}` adds the items it is credited on. ## Terms `terms` are tags with a `kind`, such as `mechanic` or `category`, and a slug you can filter by: ```bash curl "https://tablearchive.dev/v1/terms?kind=mechanic" -H "Authorization: Bearer $TA_KEY" curl "https://tablearchive.dev/v1/items?term=deck-building" -H "Authorization: Bearer $TA_KEY" ``` `GET /v1/terms` returns every term with its `item_count`. ## Relations `relations` connect items in both directions. Each has a `type`, a `direction` (`outgoing` from this item, or `incoming` from the other), and the other item's `id`, `kind`, `name`, and `slug`. | Type | A → B means | |---|---| | `expansion_of` | A expands B | | `supplement_for` | A is a sourcebook for RPG B | | `set_of` | A is a card set of trading card game B | | `edition_of` | A is an edition or printing of B | | `reimplements` | A is a redesign of B | | `integrates_with` | A and B can be combined | | `accessory_for` | A is an accessory for B | | `contains` | A is a collection that includes B | So a base game's `relations` include its expansions as `incoming` `expansion_of` entries, and an expansion's include its base game as `outgoing`. # History and provenance > Where each fact came from, every change to an item, and a feed of changes to sync against. ## Provenance Each item's `provenance` says which source set each field, and when: ```json "provenance": { "min_players": { "source": "wikidata", "url": "https://www.wikidata.org/wiki/Q17271", "at": "2026-10-05T03:17:19Z" }, "max_playtime": { "source": "shopify:catanshop.com", "url": "https://…", "at": "…" } } ``` `GET /v1/sources` lists every source with its `kind` (`manufacturer`, `open_data`, `community`, `other`), `url`, and `license`. ## An item's history ```bash curl "https://tablearchive.dev/v1/items/the-settlers-of-catan/history" -H "Authorization: Bearer $TA_KEY" ``` Returns `events`, newest first, each with a `type` (`created`, `updated`, `merged`, `photo_added`, `photo_removed`), the `changes` as from/to pairs, who submitted and approved it, and the source. Takes `limit` and `offset`. ## Syncing To keep a copy up to date, poll the global feed with the highest event id you have seen: ```bash curl "https://tablearchive.dev/v1/events?after=14172&limit=200" -H "Authorization: Bearer $TA_KEY" ``` With `after`, events come oldest first, so you can process them in order and store the last id. Without it, they come newest first. Filter by `type` to watch only, say, `photo_added`. Each event names its `item_id`, so a sync loop is: fetch events, collect the item ids, re-fetch those items. # Submissions > Adding items and correcting facts through the API, and what happens next. Everything that changes the catalog is a **submission**: a new item or an update to one. Regular users' submissions wait for an admin to approve them; bots' are published at once unless flagged. ## Create an item ```bash curl -X POST "https://tablearchive.dev/v1/submissions" \ -H "Authorization: Bearer $TA_KEY" -H "Content-Type: application/json" \ -d '{ "action": "create", "payload": { "kind": "board_game", "name": "Cascadia", "data": { "year_published": 2021, "min_players": 1, "max_players": 4, "min_playtime": 30, "max_playtime": 45, "min_age": 10 }, "credits": [{ "role": "designer", "name": "Randy Flynn" }, { "role": "publisher", "name": "Flatout Games", "kind": "organization" }], "terms": [{ "kind": "mechanic", "name": "Tile placement" }], "external_ids": [{ "source": "bgg", "id": "295947" }, { "source": "gtin", "id": "850014195151" }] }, "source_url": "https://www.flatout.games/cascadia", "note": "From the publisher's page" }' ``` Returns `201` with the submission, including its `status` (`pending` or `approved`) and, once approved, the `item_id`. A `create` whose `external_ids` match an item already in the catalog becomes an **update** of that item, so re-running an import is safe. ## Update an item Send `action: "update"` with the `item_id` and only the fields that change. `null` removes a field. ```json { "action": "update", "item_id": "fc0f21c4-…", "payload": { "data": { "max_playtime": 90, "language": null } }, "note": "Box says 75–90 minutes" } ``` Credits, terms, relations, and external IDs in an update are **added**. To take something off, list it under `remove`: ```json { "action": "update", "item_id": "…", "payload": { "remove": { "credits": [{ "role": "publisher", "entity_id": "…" }] } } } ``` Removals always wait for a human, even from a bot. ## Payload reference | Field | Notes | |---|---| | `kind`, `name` | Required for `create` | | `data` | The [known fields](https://tablearchive.dev/docs/items#fields-in-data) plus any extra keys; at most 100,000 characters as JSON | | `credits[]` | `role`, `name`, `kind` (`person` or `organization`), optional `external_ids`, or `entity_id` to credit an existing entity | | `terms[]` | `kind` and `name` | | `relations[]` | `type` plus either `item_id` or an `external_id` of the other item | | `external_ids[]` | `source`, `id`, optional `url`. Barcodes are validated | | `remove` | Lists of credits, terms, relations, external_ids, and image ids to take off | Top-level: `action`, `item_id` (for updates), `payload`, `source_url` (where the facts came from), and a `note` for the reviewer. ## Your submissions `GET /v1/submissions` lists yours, with `status` and the reviewer's `review_note`. Filter with `status=pending`. `GET /v1/submissions/{id}` returns one. ## What bots may do A bot's unreviewed update only fills empty fields or refreshes fields its own source set earlier. It never overwrites another source's value. New items with the same name and kind as an existing one are flagged as possible duplicates and wait for review. # Uploading photos > Adding a photo to an item or to your pending submission. ```bash curl -X POST "https://tablearchive.dev/v1/images" \ -H "Authorization: Bearer $TA_KEY" \ -F "item_id=fc0f21c4-…" \ -F "kind=box_front" \ -F "file=@catan-box.jpg" \ -F "license=CC BY-SA 4.0" \ -F "attribution=Your Name" ``` `201` means a new photo; `200` means this exact file was already on the item. ## Fields | Field | Notes | |---|---| | `file` | Required. JPEG, PNG, WebP, GIF, or AVIF | | `item_id` or `submission_id` | Exactly one: attach to an item, or to your own pending submission | | `kind` | `box_front`, `box_back`, `box_side`, `components`, `detail`, `gameplay`, `other` (default) | | `caption` | Up to 500 characters | | `sort_order` | Lower first | | `source_page_url` | The page the photo came from, if not your own | | `source_url` | The original image file, if any | | `license` | e.g. `CC BY 4.0`, or `own work` | | `attribution` | Who to credit | ## Limits - At most 10 MB and 40 megapixels per file. Larger files get `413`. - Files over 1.5 MB are stored as WebP at most 1024 px on a side; smaller ones are kept as uploaded. - Regular users' photos wait for an admin; bots' and admins' are published at once. - 200 submissions and uploads per hour for regular users. ## Licensing Only upload photos you took or that carry a license allowing redistribution, and say which. Publisher photos need the publisher's permission. See [Sources and licenses](https://tablearchive.dev/docs/sources-licenses). # Sources and licenses > Where the data comes from, and what you may do with it. ## The database and API Our own work is released under [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/): the database as a compilation, the API, and the facts contributed here. That means public domain: use it in your own apps, commercial or not, with no attribution required and no warranty of any kind. CC0 covers only what we hold the rights to. Facts, descriptions, and photos taken from other sources keep those sources' licenses, listed below and returned with the data. If you show our data, carrying the source and license along is the right thing to do, and for some sources it is required. ## The facts Facts come from four kinds of source, each listed by `GET /v1/sources` with its `license`: | Kind | Examples | Terms | |---|---|---| | `open_data` | Wikidata, Wikimedia Commons | CC0 and Creative Commons; carry the license and attribution along | | `manufacturer` | Publishers' shops and product pages | Factual data; descriptions and photos are the publisher's and shown with permission | | `community` | People submitting through this site | Contributed to be shared under the same terms as the rest | | `other` | Card databases such as Scryfall | Each has its own terms, linked from the source | Each field's source is in the item's `provenance`, so you can tell where a number came from and decide whether to use it. ## Photos Each photo has its own `license` and `attribution` in the API. Carry both along wherever you show it. A null license means all rights reserved: show it only as part of using this API. ## Contact Questions about a source or a takedown: use the [contact page](https://tablearchive.dev/contact). # llms.txt > Files written for AI assistants and coding agents. Everything on this site is also available as plain Markdown, so an assistant can read it without scraping HTML. | File | What it is | |---|---| | [/llms.txt](https://tablearchive.dev/llms.txt) | A short index: what the API is, how to authenticate, and a link to every docs page with a one-line summary. Follows [llmstxt.org](https://llmstxt.org) | | [/llms-full.txt](https://tablearchive.dev/llms-full.txt) | Every docs page in one Markdown file, for assistants that take a single document | | `/docs/.md` | One docs page as Markdown, e.g. [/docs/quick-start.md](https://tablearchive.dev/docs/quick-start.md). Sending `Accept: text/markdown` to the HTML address returns the same | | [/openapi.json](https://tablearchive.dev/openapi.json) | The OpenAPI 3 document. See [OpenAPI](https://tablearchive.dev/docs/openapi) | ## Using them Paste `https://tablearchive.dev/llms.txt` into the assistant's context, or into a project's instructions file (`CLAUDE.md`, `.cursorrules`, `AGENTS.md`). For a one-shot task, `llms-full.txt` saves the assistant a round trip. Each docs page has a **Copy as Markdown** button at the top for pasting one page. See [Prompting tips](https://tablearchive.dev/docs/prompting-tips) for what to say. # OpenAPI > The machine-readable description of the API, for typed clients and agents. The API is described by an OpenAPI 3 document at [/openapi.json](https://tablearchive.dev/openapi.json). It covers every public endpoint with its parameters, request bodies, and the `Authorization` scheme. Endpoints that belong to the web app only (accounts, admin) are left out. ## Generate a client ```bash npx openapi-typescript https://tablearchive.dev/openapi.json -o tablearchive.d.ts ``` ```python pip install openapi-python-client openapi-python-client generate --url https://tablearchive.dev/openapi.json ``` ## Try it in the browser The [API explorer](https://tablearchive.dev/api) renders the same document with Swagger UI. Click **Authorize**, paste a key, and every endpoint can be run from the page, photo uploads included. ## Give it to an agent Tool-using agents can load the document directly: it is small, has one security scheme (`bearer`), and uses plain JSON everywhere except photo uploads (`multipart/form-data`). Pair it with [llms.txt](https://tablearchive.dev/docs/llms-txt) so the agent also knows the conventions the schema cannot express, such as which filters combine and what `popular` sorts by. # Prompting tips > How to describe TableArchive to an AI assistant so it writes working code first time. ## Give it the facts Start your request with a paragraph like this, or point the assistant at [llms.txt](https://tablearchive.dev/llms.txt): > TableArchive is a JSON API for board game, card game, and RPG facts at `https://tablearchive.dev/v1`. Every request needs `Authorization: Bearer $TA_KEY`. `GET /v1/items?q=` searches; filters are `kind`, `players`, `max_playtime`, `age`, `year`, `term`, `entity`, `external_id`. `GET /v1/items/{slug}` returns one item with `data`, `credits`, `relations`, `images`, and `external_ids`. Responses are `{ total, items }`. Errors are `{ error }`. Docs: `https://tablearchive.dev/llms-full.txt`. ## Things assistants get wrong - **Players is exact.** `players=2` means playable with two, not "two or more". Say so if your app means something else. - **Editions are hidden by default.** A plain search returns games, not printings. Ask for `kind=edition` or search by barcode to get them. - **Photo links expire.** Have the code fetch the item again rather than store `thumb_url`. - **Keys stay server-side.** Ask for a small proxy or serverless function if the app runs in a browser. - **Rate limits.** Ask for a wait on `429` using `Retry-After`. ## A prompt that works > Build a small web page where I scan a barcode with my phone camera and see the game's name, players, playtime, and box photo from TableArchive. Use `GET /v1/items?external_id=`. Keep my API key in a serverless function. Here are the docs: https://tablearchive.dev/llms-full.txt ## Coming later An MCP server, so agents can call search and lookup as tools without writing HTTP code.