# Veritura buyer API > Read compounding pharmacy and supplier files from your own software: every item with who checked it, when, and the receipt (the source asked, the query, and what it returned). Free to read. Test keys read a fixed sample with invented names; live keys read published files, your watches and your private lookups. Webhooks are on a paid plan. Key facts: - Base URL: https://www.veritura.co - Auth: `Authorization: Bearer vk_test_...` or `vk_live_...`, scope `network:read`. Make a key in Settings, Developer. A key is shown once; Veritura stores only its SHA-256 fingerprint. Keep keys on your server. - Versions: send `X-Veritura-Version: 2026-10-01`. Every answer carries `X-Veritura-Version` with the version it was answered under; with no header, the latest stable version answers. An unknown version is a 400 `version_unknown` whose `versions` field lists the accepted versions. A breaking change ships only under a new dated version; the version it replaces keeps working for at least 12 months, is announced in the changelog the day the new one ships, and its answers carry `Deprecation`, `Sunset` and `Link` headers from then on. - Pagination: every list takes `limit` and `cursor` and answers with `next_cursor` (null on the last page). Send `next_cursor` back as `cursor`. A cursor marks a place, not a count, so rows added while you page never repeat or skip a row that was already there. It is opaque and bound to the list, its filters and the key's mode (otherwise 400 `cursor_invalid`). `offset` on `/v1/pharmacies` and `/v1/suppliers` is deprecated, works until at least 2027-10-01, and answers with `Deprecation`, `Sunset` and `Link` headers; never send `cursor` and `offset` together. - Idempotency: `POST /v1/lookups` and `POST /v1/watches` take an `Idempotency-Key` header (1 to 255 visible characters; a UUID works). For 24 hours, the same key and body return the first successful answer again with `Idempotent-Replayed: true`, and nothing runs twice. The same key with a different body is a 409 `idempotency_key_reused`; while the first request still runs, a retry is a 409 `idempotency_in_progress` with `Retry-After: 1`. Errors are not kept, so a refused request can be fixed and sent again with the same key. - Rate limit: 300 requests a minute per workspace, counted apart for test and live keys; running a lookup has its own 10 a minute. Headers on every answer: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` (Unix seconds). A 429 carries `Retry-After` (seconds). - Every success is `{ "ok": true, "mode": "test" | "live", ... }`. Every error is `{ "error": "...", "code": "..." }`. Codes: version_unknown, cursor_invalid, idempotency_key_invalid, bad_request, own_file, key_missing, key_malformed, key_invalid, key_revoked, key_expired, key_issuer_removed, scope_missing, demo, not_found, method_not_allowed, idempotency_key_reused, idempotency_in_progress, rate_limited, lookup_failed. - Retrying: a dropped connection or timeout, 502, 503, 504, 409 `idempotency_in_progress`, and 429 after `Retry-After`. Retry a write only with the same `Idempotency-Key`. - Lists are alphabetical unless they say otherwise (watches oldest first and their changes newest first; exposure in the order the products were named). Nothing is ranked, graded or scored. In a file, findings come first. - Who checked each item: `checkedBy` is `public_source` (a lookup Veritura ran at the FDA, openFDA, NPPES or OIG), `independent_person` (someone independent of the firm opened the issuer's record), `self` (the firm's own staff recorded it), `document` (Veritura read the document against its rule) or `claim` (typed in, nobody checked it). `checkedByWord` says the same in words. `howWord` says how the item was established ("Checked at the source", "Read from its document, not checked at the issuer", "Confirmed by " ...) and `howMark` gives it as a key (`source`, `person`, `review`, `doc`, `self`, `claim`, `missing`, `bad`); only `source` and `person` earn a check mark, and a verified item's `stateWord` uses the same words, never a bare "Confirmed". An item the firm recorded about itself reads `state: "self_recorded"` and is never counted in `completeness.checked` or `completeness.confirmed`. Each item carries its `receipt`. - Documents: an item with a document on file carries `document` with `access` (`public`: anyone opens it at `url`; `connected`: a buyer opens it after asking and the firm allowing, each open recorded; `private`: never opened by others), `accessWord` ("Open", "Ask for access" or empty), `name`, `keptWord`, `at` and `url` (set only when public). A file also carries `documents` and `documentsLine`, and a directory row `documents`. A private document is listed without its name; the item, how it was established and any finding are shown whatever the firm chooses. `route` `attested` is a document Veritura could not read, kept and attested by the person who added it while a Veritura reviewer reads it; `reviewed` is one a Veritura reviewer read and found to be what the item asks for. - For telehealth buyers, every file also carries `telehealth` (LegitScript for the website, states licensed, the pharmacist in charge with the OIG screen, the "essentially a copy" practice) and `exposure` (where each product stands on the FDA's public lists, with each list's source and date). Facts, never a verdict. - What it is not: Veritura verifies paperwork, not medicine. A file is not an approval, a certification or a rating. Veritura does not sell medicine, route orders or rank pharmacies. ## Docs - [Buyer API docs](https://www.veritura.co/buyer-api): keys, modes, versioning, pagination, idempotency, rate limits, every endpoint with curl, TypeScript and Python, the file.changed webhook, errors, the changelog - [Buyer API docs as Markdown](https://www.veritura.co/buyer-api.md): the same page, as one Markdown file - [OpenAPI 3.1](https://www.veritura.co/buyer-api/openapi.json): the machine-readable contract for every endpoint, header and the webhook - [TypeScript client](https://www.veritura.co/buyer-api/client.ts): one file, no dependencies, typed responses, page iterators, the version header, idempotency keys, retries and webhook signature checks - [Python client](https://www.veritura.co/buyer-api/client.py): one file, standard library only, Python 3.8 or later, the same methods ## Endpoints - `GET /v1/pharmacies?q=&state=&npi=&limit=&cursor=`: published pharmacy files, alphabetical - `GET /v1/pharmacies/{id}/file`: one published pharmacy file (id: the file id vfl_... or its public name) - `GET /v1/suppliers?q=&state=&limit=&cursor=`: published supplier files, alphabetical - `GET /v1/suppliers/{id}/file`: one published supplier file - `GET /v1/lookups?limit=&cursor=`: the files your workspace keeps about pharmacies, private to your workspace - `POST /v1/lookups` with `{"q":"","state":"AZ","watch":true}` and an `Idempotency-Key`: run a lookup of any pharmacy, or refresh one, privately; 10 a minute per workspace - `GET /v1/lookups/{id}`: one private lookup, item by item - `GET /v1/exposure?product=...&product=...&limit=&cursor=`: where each product stands on the FDA's public lists, by product (add `lookup=` or `pharmacy=` for what the FDA said about that pharmacy); `counts` covers every product named - `GET /v1/watches?limit=&cursor=`: your watches (oldest first) and what changed (newest first); one cursor pages both - `POST /v1/watches` with `{"kind":"pharmacy","id":"..."}` and an `Idempotency-Key`: watch a published file; `{"kind":"lookup","id":"vfl_..."}` watches one of your own lookups, kept current by the weekly look-up - `DELETE /v1/watches/{id}`: stop watching ## Webhook - `file.changed`: the body is `{ id, event: "file.changed", created, attempt, data: { event, file: { id, kind, name, url }, changes: [{ type, item, line }], at } }`. Check `X-Veritura-Signature`, which is `sha256=` plus the hex HMAC-SHA256 of the raw body with your endpoint's secret, before trusting it. The event id is the same on every redelivery. Webhooks are on a paid plan; reading, watching and polling `GET /v1/watches` are free. ## Optional - [Changelog](https://www.veritura.co/buyer-api#changelog): dated changes