# Pricelists.org — full agent reference Machine-readable price comparison API. This file is the complete reference in markdown; the authoritative machine-readable contract is [openapi.json](https://pricelists.org/.well-known/openapi.json). Base URL: `https://pricelists.org` --- ## 1. Authentication One call, no approval step: ```http POST /api/agent/v1/register Content-Type: application/json {"name": "my-agent"} ``` ```json { "apiKey": "plk_5f3a9c1e2b7d4a8f6c0e1d2b", "name": "my-agent", "rateLimitPerMinute": 60 } ``` The key is returned **once**. Only its SHA-256 hash is stored, so a lost key cannot be recovered — register again. Send it on every authenticated call: ``` Authorization: Bearer plk_5f3a9c1e2b7d4a8f6c0e1d2b ``` Registration is limited to 5 requests per minute per IP. --- ## 2. Search — `POST /api/agent/v1/search` No authentication. 30 requests per minute per IP. ### Request ```json { "identifiers": {"gtin": ["5901234123457"]}, "query": "running shoes size 42", "limit": 10, "currency": "PLN", "locale": "pl", "shipTo": {"country": "PL", "postalCode": "00-001"}, "rankings": ["cheapest", "best"], "bestWeights": {"sellerCredibility": 0.8, "price": 0.15, "deliverySpeed": 0.05}, "filters": {"availability": "in_stock_only"} } ``` Identifier lookups are exact and are tried first. `query` falls back to the search index. Supplying both is fine — identifiers win when they match. `limit` accepts 1–50 and defaults to 10. ### Ranking modes - `cheapest` — price only, ascending. - `best` — weighted blend of merchant credibility, price and delivery speed. Defaults lean heavily on credibility (0.8/0.15/0.05); override per request with `bestWeights`. ### Response ```json { "requestId": "req_01HX...", "rankings": { "cheapest": {"offers": [ /* Offer objects */ ], "nextCursor": null}, "best": {"offers": [ /* Offer objects */ ], "nextCursor": null} } } ``` `nextCursor` is reserved for pagination and is currently always `null`; one response returns the full ranked set up to `limit`. ### Offer object ```json { "offerId": "of_12345", "product": { "id": "prod_987", "title": "Running shoes Alpha", "identifiers": {"gtin": "5901234123457", "mpn": null}, "imageUrl": "https://.../alpha.jpg" }, "merchant": { "id": "m_42", "name": "SklepBiegacza", "country": "PL", "credibilityScore": 78, "website": "https://example.com", "signals": {"ratingAvg": 4.6, "ratingCount": 1203} }, "pricing": { "currency": "PLN", "itemPrice": 199.00, "shippingPrice": null, "taxEstimate": null, "dutiesEstimate": null }, "availability": {"status": "in_stock", "quantity": 7, "checkedAt": "2026-07-20T09:12:00+00:00"}, "delivery": {"type": "shipping", "minDays": 1, "maxDays": 3, "minDate": "2026-07-21", "maxDate": "2026-07-23"}, "execution": {"supported": true, "mode": "api", "fallbackMode": "checkout_url"}, "links": {"offerUrl": "https://...", "checkoutUrl": "https://..."}, "scores": null } ``` `credibilityScore` runs 0–255, higher is more trusted, and defaults to 50 when unknown. `shippingPrice`, `taxEstimate` and `dutiesEstimate` are not yet computed and are always `null` — do not present them as zero. --- ## 3. Quote — `POST /api/agent/v1/quote` Bearer required. Converts an offer into a price snapshot valid for **10 minutes**. A quote is mandatory before executing. ```json {"offerId": "of_12345", "currency": "PLN", "shipTo": {"country": "PL"}} ``` ```json { "quoteId": "q_01HX...", "expiresAt": "2026-07-20T09:22:00+00:00", "offer": { /* Offer object */ }, "final": {"currency": "PLN", "itemPrice": 199.00, "total": 199.00}, "execution": {"supported": true, "mode": "api", "fallbackMode": "checkout_url"} } ``` --- ## 4. Execute — `POST /api/agent/v1/execute` Bearer required. **`Idempotency-Key` header required.** ```http POST /api/agent/v1/execute Authorization: Bearer plk_... Idempotency-Key: 3f9c1e2b-7d4a-8f6c-0e1d-2b5a9c3f1e2b Content-Type: application/json {"quoteId": "q_01HX...", "confirm": true, "shipTo": {"country": "PL"}} ``` `confirm` must be exactly `true`. Replaying a request with the same `Idempotency-Key` returns the original order rather than creating a second one — always send a fresh key per intended purchase, and reuse it on retries. ### Three possible outcomes **Order placed** ```json {"ok": true, "orderId": "ord_01HX...", "status": "placed", "merchantOrderRef": "M-99812"} ``` **Hand-off required** — the agent must give the URL to the human: ```json {"ok": false, "status": "requires_action", "action": {"url": "https://merchant/verify/..."}} ``` **Merchant has no programmatic checkout**: ```json {"ok": false, "status": "not_supported", "fallback": {"checkout_url": "https://merchant/cart/..."}} ``` `not_supported` is not an error — it is the expected path for most merchants. Present the checkout URL to the user rather than reporting a failure. ### Safety rules - Never request or transmit card numbers, CVV codes or one-time passwords. Payment always happens on the merchant's side. - Always quote before executing; never construct an execute call from a search result directly. - Confirm the purchase with the user before sending `confirm: true`. --- ## 5. Order status — `GET /api/agent/v1/orders/{orderId}` Bearer required. Scoped to the calling key. ```json { "orderId": "ord_01HX...", "status": "placed", "executionMode": "api", "checkoutUrl": null, "receipt": {"total": 199.00, "currency": "PLN"}, "tracking": {"carrier": "DPD", "number": "..."} } ``` --- ## 6. Submissions ### `POST /api/agent/v1/submit` Bearer required. Submits a pricelist for human moderation. Nothing is published until approved. ```json { "merchant": {"name": "My Shop", "website": "https://myshop.example", "country": "PL"}, "items": [ {"title": "Running shoes Alpha", "gtin": "5901234123457", "price": 199.00, "currency": "PLN", "url": "https://myshop.example/p/1", "inStock": true} ] } ``` ```json {"submissionId": "sub_01HX...", "status": "pending_review", "itemCount": 1, "message": "Submission queued for review."} ``` ### `GET /api/agent/v1/submissions/{submissionId}` Returns review status with per-item outcomes. An agent sees only its own submissions. --- ## 7. Errors | Status | Meaning | Agent action | |---|---|---| | 400 | Missing `Idempotency-Key` on execute | Add the header and retry | | 401 | Missing or invalid key | Re-register or fix the header | | 404 | Not found, or not visible to this key | Do not retry | | 410 | Quote expired | Request a new quote | | 422 | Validation failed (`errors` maps field → messages) | Fix the payload | | 429 | Rate limited | Back off; honour `Retry-After` | Error bodies carry `message` and, where applicable, `code`. --- ## 8. Rate limits | Scope | Limit | Keyed by | |---|---|---| | Search and other public endpoints | 30/min | IP | | Registration | 5/min | IP | | Authenticated endpoints | 60/min (per-key configurable) | API key | --- ## 9. Crawling instead of calling Every product page carries schema.org JSON-LD (`Product` with `AggregateOffer`, plus `BreadcrumbList`), so a crawler can extract structured offers without touching the API. - Product pages: `https://pricelists.org/{pl|en}/price/{slug}` - Merchant pages: `https://pricelists.org/{pl|en}/price-list/{slug}` - Sitemaps are listed in [robots.txt](https://pricelists.org/robots.txt) `robots.txt` explicitly allows GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, Claude-User, Claude-SearchBot, PerplexityBot, Google-Extended, Applebot-Extended and CCBot. --- ## 10. Data quality — read before quoting prices to a user A large share of catalogue entries come from a GTIN catalogue import that generated **placeholder prices rather than merchant-confirmed ones**. These are not distinguishable in the current API response. Practical guidance: - A product with offers from **several merchants** is far more likely to carry real prices. - A single-offer product page should be treated as indicative, not authoritative. - Prefer offers whose merchant has a non-default `credibilityScore` and a non-zero `ratingCount`. Real merchant data is being backfilled; this section will be removed once the placeholder prices are replaced.