{"openapi":"3.1.0","info":{"title":"BotMarket","description":"A data marketplace for bots, AI agents, and automated research workflows. Datasets cover international trade (BACI, US Customs), US demographics (ACS), and international debt statistics. All queries are free — claim a free API key at POST /api/promo/claim.","version":"1.0.0"},"paths":{"/api/datasets/{slug}/sample":{"get":{"tags":["datasets"],"summary":"Get Sample","description":"Free sample of the dataset (up to 100 rows). No authentication required.\nServed from sample.csv in DATA_OUT_DIR when present, else from full.parquet LIMIT.","operationId":"get_sample_api_datasets__slug__sample_get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/datasets/{slug}/query":{"get":{"tags":["datasets"],"summary":"Query Dataset","description":"Query a dataset with optional column filters. Authenticate with a free BotMarket API key (prefix bot_market_ak_,\n    claim one at POST /api/promo/claim), or an OEC API token (32 lowercase hex characters) for eligible subscribers.\n\n    BotMarket is free — queries cost nothing.\n\n    Filter columns are dataset-specific and listed in /api/datasets/{slug} as query_filters.\n    Multiple values for a filter use SQL IN: pass the same param repeatedly\n    (?geo=ES&geo=FR) or comma-separated (?geo=ES,FR).\n\n    Example:\n        GET /api/datasets/ilostat-key-metrics/query?ref_area=US&year=2023&limit=50\n        GET /api/datasets/ilostat-key-metrics/query?ref_area=US&ref_area=CA&year=2023\n        Authorization: Bearer bot_market_ak_<your_key>","operationId":"query_dataset_api_datasets__slug__query_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"description":"Rows to return","default":1000,"title":"Limit"},"description":"Rows to return"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Rows to skip","default":0,"title":"Offset"},"description":"Rows to skip"},{"name":"format","in":"query","required":false,"schema":{"type":"string","description":"Response format: json or csv","default":"json","title":"Format"},"description":"Response format: json or csv"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/catalog":{"get":{"tags":["catalog"],"summary":"List or search catalog","description":"**GET /api/catalog** — List all datasets (paginated).\n\n**GET /api/catalog?q=<terms>** — Search by free text. Each term in `q` is matched against name, description, slug, domain, scope, and tags. Matching is fuzzy (typos allowed via pg_trgm). All terms must match (AND). Results are ranked by relevance (name/slug > description > rest).\n\n**Without `q`:** Returns datasets ordered by domain, scope, geo, slug. Optional filters: `domain`, `scope`, `geo`, `tag`.\n\n**With `q`:** Returns datasets matching the search terms. Optional filters: `domain`, `scope`, `tag` (same semantics). `geo` is not applied when searching.\n\n**Response (both):** `{ \"total\", \"limit\", \"offset\", \"domains\", \"scopes\", \"datasets\" }`. Each dataset is a summary; use the detail URL for full schema, query_filters.","operationId":"get_catalog_api_catalog_get","parameters":[{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional search terms (space-separated). When provided, filters datasets by free text over name, description, slug, domain, scope, tags (fuzzy/typo-tolerant). When omitted, returns all datasets.","title":"Q"},"description":"Optional search terms (space-separated). When provided, filters datasets by free text over name, description, slug, domain, scope, tags (fuzzy/typo-tolerant). When omitted, returns all datasets."},{"name":"domain","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by domain (e.g. economics)","title":"Domain"},"description":"Filter by domain (e.g. economics)"},{"name":"scope","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by scope (e.g. global)","title":"Scope"},"description":"Filter by scope (e.g. global)"},{"name":"geo","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by geo (ISO-3 for country scope, or region slug); only when not using q","title":"Geo"},"description":"Filter by geo (ISO-3 for country scope, or region slug); only when not using q"},{"name":"tag","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by tag (exact)","title":"Tag"},"description":"Filter by tag (exact)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Page size","default":24,"title":"Limit"},"description":"Page size"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Number of datasets to skip","default":0,"title":"Offset"},"description":"Number of datasets to skip"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/catalog/summary":{"get":{"tags":["catalog"],"summary":"Catalog overview grouped by domain","description":"Returns a preview of the catalog grouped by domain — useful for bots and humans\nwho want a quick overview of what data is available before drilling into a\nspecific domain.\n\nEach domain entry includes:\n- **`domain`** — domain slug (e.g. `trade`, `economics`)\n- **`total`** — total number of datasets in this domain\n- **`preview`** — number of datasets returned in this response (up to 12)\n- **`more_url`** — URL to fetch **all** datasets in this domain\n  (`GET /api/catalog?domain=<domain>`)\n- **`datasets`** — the first `preview` datasets, same schema as\n  `GET /api/catalog`\n\nTo retrieve all datasets for a domain, follow `more_url`:\n```\nGET /api/catalog?domain=trade&limit=100&offset=0\n```\n\nTo list all domains with full pagination, use `GET /api/catalog` directly.","operationId":"get_catalog_summary_api_catalog_summary_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/datasets/{slug}":{"get":{"tags":["datasets"],"summary":"Get Dataset","description":"Full dataset detail including schema, access, and source information.","operationId":"get_dataset_api_datasets__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/datasets/{slug}/members/{col}":{"get":{"tags":["datasets"],"summary":"Get Members","description":"Return distinct values (members) for a filter column with dataset context.","operationId":"get_members_api_datasets__slug__members__col__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"col","in":"path","required":true,"schema":{"type":"string","title":"Col"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/orders/{order_id}":{"get":{"tags":["account"],"summary":"Get Order","description":"Look up a past order to recover its details.\n    Requires ?buyer_email= matching the email used when the order was created.\n    When status='paid', add_balance orders include the api_key (shown once, then cleared).","operationId":"get_order_api_orders__order_id__get","parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Order Id"}},{"name":"session_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session Id"}},{"name":"buyer_email","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Buyer Email"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/account/lookup":{"post":{"tags":["account"],"summary":"Lookup By Email","description":"Look up all orders and API keys for an email address.\nValidates format and existence (at least one order).\nSends a summary email to that address via Mailgun.\n\nFor agents: POST with JSON body `{\"email\": \"user@example.com\"}`.\nFor humans: POST form with `email` field (from web page).","operationId":"lookup_by_email_api_account_lookup_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/account/balance":{"get":{"tags":["account"],"summary":"Get Balance","description":"Return account info for the authenticated API key.\n    Read-only — never changes your account. BotMarket is free.\n\n    Authorization: Bearer bot_market_ak_<key>  or  Bearer <OEC API token> (32 lowercase hex)\n    Query param: buyer_email=<email>  (required — must match the email used when the key was\n    claimed, or for OEC tokens the email on the linked OEC account)\n\n    Returns: { api_key_prefix, balance_usd, label, auth_source }","operationId":"get_balance_api_account_balance_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/promo/claim":{"get":{"tags":["account"],"summary":"Claim your free API key (GET)","description":"GET alternative to POST /api/promo/claim — for agents that cannot send a request body.\n\nPass all fields as query parameters:\n```\nGET /api/promo/claim?buyer_email=you@example.com\n```\n\nSame rules and response as the POST version.","operationId":"claim_promo_get_api_promo_claim_get","parameters":[{"name":"buyer_email","in":"query","required":true,"schema":{"type":"string","description":"Your email address — receipt and key delivered here.","title":"Buyer Email"},"description":"Your email address — receipt and key delivered here."},{"name":"buyer_name","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Buyer Name"}},{"name":"buyer_company","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Buyer Company"}},{"name":"buyer_role","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Buyer Role"}},{"name":"agent_name","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agent Name"}},{"name":"agent_goal","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agent Goal"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["account"],"summary":"Claim your free API key (POST)","description":"Get your free BotMarket API key. BotMarket is free — every query is free.\n\n    Rules:\n    - One key per email address — subsequent claims from the same email are rejected.\n\n    **Tip for agents that cannot send a request body:** use the GET version instead —\n    `GET /api/promo/claim?buyer_email=you@example.com`","operationId":"claim_promo_api_promo_claim_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromoClaimRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/survey/{token}":{"get":{"tags":["survey"],"summary":"Survey Form","operationId":"survey_form_survey__token__get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["survey"],"summary":"Survey Submit","operationId":"survey_submit_survey__token__post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_survey_submit_survey__token__post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"Body_survey_submit_survey__token__post":{"properties":{"use_case":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Use Case"},"datasets_useful":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Datasets Useful"},"got_value":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Got Value"},"would_pay_for":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Would Pay For"},"feedback":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Feedback"},"cf_turnstile_response":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cf Turnstile Response"}},"type":"object","title":"Body_survey_submit_survey__token__post"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"PromoClaimRequest":{"properties":{"buyer_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Buyer Name"},"buyer_company":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Buyer Company"},"buyer_role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Buyer Role"},"agent_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agent Name"},"agent_goal":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agent Goal"},"buyer_email":{"type":"string","title":"Buyer Email"}},"type":"object","required":["buyer_email"],"title":"PromoClaimRequest"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}},"securitySchemes":{"HTTPBearer":{"type":"http","description":"BotMarket API key: prefix `bot_market_ak_` — free (claim one at POST /api/promo/claim). If you have an active OEC subscription, you can instead send your OEC API key (32 lowercase hex characters, no prefix) in the same Bearer field. View or copy your OEC API key at https://oec.world/en/account.","scheme":"bearer"}}},"tags":[{"name":"catalog","description":"Browse and search the dataset catalog."},{"name":"datasets","description":"Dataset detail, schema, filter members, sample rows, and queries (free)."},{"name":"account","description":"API key and account lookup."}]}