Start here
No key needed for the first taste.
# Real data, no account (25 results/call, 250 calls/day):
curl 'https://primat.nu/api/v3/demo/products?q=banan'
# Which stores serve a place? The site's own resolver:
curl 'https://primat.nu/api/v3/demo/stores/resolve?place=Uppsala'
# An API key in one call — no email round-trip to start:
curl -X POST https://primat.nu/api/v3/signup \
-H 'Content-Type: application/json' \
-d '{"email": "you@example.com"}'
The key is provisional (7-day TTL, tight quota). One click on the
confirmation mail (POST /api/v3/confirm/request)
upgrades it to the free tier. Machine-readable contract:
https://primat.nu/api/v3/openapi.json — self-describing root:
/api/v3.
Authentication
Authorization: Bearer <key> or
X-API-Key: <key>. Two key types:
secret (primat_live_…, server-side) and
publishable (primat_pk_…, safe to embed
in client-side apps: locked to your web origins, read-only, own
quota, CORS enabled). Manage keys at
primat.nu/konto.
Endpoints
v3 — the current surface. v1/v2 keep serving frozen for existing integrations.
-
GET /api/v3no key -
GET /api/v3/openapi.jsonno key -
GET /api/v3/demo/productsno key -
GET /api/v3/demo/stores/resolveno key -
POST /api/v3/signupno key -
POST /api/v3/confirm/request -
GET /api/v3/me -
GET /api/v3/stores -
GET /api/v3/stores/resolve -
GET /api/v3/products -
GET /api/v3/products/{chain}/{store_id}/{product_id} -
GET /api/v3/prices -
GET /api/v3/deals -
GET /api/v3/cheap -
POST /api/v3/batch -
GET /api/v3/history/{chain}/{store_id}/{product_id} -
GET /api/v3/history/{gtin}
Price semantics
Straight from the contract — the same text agents read in the OpenAPI document.
Price FACTS. regular: what everyone pays (public campaigns included). member: only set while a member price runs. multiprice/member_multiprice: PER-UNIT price when buying `quantity` (7 with quantity 5 = '5 for 35 kr'). comparison: unit price per kg/l/st computed on the REGULAR price. offer: the active campaign behind the current pricing, null when none. effective: least price a card-carrying shopper pays right now (a convenience fold; combine the facts yourself for anything serious). previous: on /deals results.
Fields: regular, member, multiprice, member_multiprice, comparison, offer, effective, previous.
Timestamps: changed_at = when the price last CHANGED,
confirmed_at = when the store was last checked. No
change since changed_at means confirmed unchanged, not
unmeasured.
Plans
Prices ex VAT. Sign up and upgrade at primat.nu/data.
| Plan | Price | Rate | Rows | History | Image URLs |
|---|---|---|---|---|---|
| Gratis | free | 60/min | 20,000 rows/day | — | — |
| App | 249 SEK/mo | 250/min (+250 burst) | 100,000 rows/day | — | yes |
| Pro | 1995 SEK/mo | 600/min (+600 burst) | 1,000,000 rows/day | 12 mo | yes |
| Byrå | 7495 SEK/mo | 3000/min (+3000 burst) | unlimited | full | yes |
Errors
Every non-2xx answer is
{"error": {"code", "message"}} with honest
Retry-After/WWW-Authenticate headers.
Codes: invalid_api_key, rate_limited, daily_row_budget_exceeded, demo_limit_reached, query_required, bad_request, not_found, product_not_found, place_not_found, upgrade_required, origin_not_allowed, read_only_key, email_in_use, invalid_email, signup_limit_reached, history_timeout, service_unavailable.
Questions, bulk exports (CSV/Parquet, Byrå plan), or something missing? kontakt@primat.nu.