The API returns the same data as the website: Prozorro.Sale lots, their changes and your saved filters — handy for pulling lots into your CRM, spreadsheet or website. Read-only, JSON responses. Lot titles and labels are in Ukrainian by default; add ?lang=en for English labels (titles in English where the organiser provided them).
Key and first request
Create a key in your account, on the API tab. It is shown only once — copy it right away. Send it in a header:
curl -H "Authorization: Bearer prs_your_key" \
"https://parser.randomstar.org/api/v1/lots?kind=land_lease&per_page=5&lang=en"
Instead of Authorization you may send X-Api-Key: prs_…. Keys in the URL are not accepted: from there they end up in logs and browser history. Treat the key like a password; if it leaks, revoke it in your account and create a new one. You can have up to five keys.
Limits
The limit is requests per day per account (shared by all its keys), reset at midnight Kyiv time. Reference lists (/kinds, /regions, /statuses) are free and need no key.
| Plan | Requests per day |
|---|---|
| Pro | 1,000 |
| Business | 20,000 |
Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (Unix time). Over the limit — 429 with Retry-After in seconds.
Methods
| Request | Returns |
|---|---|
GET /api/v1/lots | Lot search, paginated |
GET /api/v1/lots/UA-… | One lot: all fields, change history, other auctions of the same asset |
GET /api/v1/changes | Change feed (new lots, price, dates, status, documents, result) for syncing |
GET /api/v1/filters | Your saved filters |
GET /api/v1/filters/id/lots | Lots for your filter — the same ones the bot sends you |
GET /api/v1/me | Key owner, plan, limit and usage today |
| Webhook | We push new lots and changes to your address |
GET /api/v1/kinds, /regions, /statuses | Code lists (no key needed) |
Lot search: /lots
| Parameter | Value |
|---|---|
q | Words from the title or locality; a UA-… auction number or a cadastral number searches exactly |
kind | Auction type code from /kinds, e.g. land_lease |
region | Region as in /regions (Ukrainian), e.g. Полтавська |
min, max | Starting price, UAH |
deal | discount — only discounted repeat auctions, dutch — only Dutch auctions (the price goes down) |
status | open (default — accepting or about to accept bids), all, or codes from /statuses, comma-separated |
published_since, modified_since | ISO 8601 date or date-time |
sort | new (default), modified, deadline, price_asc, price_desc |
page, per_page | Page and size (up to 100, default 20) |
Each lot has id (auction number), url (our lot page), source_url (Prozorro.Sale), kind, status, title, region, locality, price (amount, currency, period for leases, vat_included), discount_percent, area_ha, area_m2, cadastral, dates (published, modified, tender_end, auction_start, ISO 8601 with Kyiv offset) and cancelled; meta holds page, per_page, total, pages.
Lot details: /lots/UA-…
Adds title_uk, title_en, auction_type, item_type, location, guarantee, min_step, normative_value, land_purpose, lease_duration, seller (name and EDRPOU — legal entities and authorities only), platform, attempt, previous_auction_id, documents_count, bids_count, result_amount, plus changes (last 50) and same_object_auctions.
Change feed: /changes
Parameters: since_id (cursor; without it — the last 24 hours), limit (up to 500), events (new, status, price, dates, docs, cancelled, result), and the search conditions q, kind, region, min, max, deal. Store meta.next_since_id and pass it next time; if meta.has_more is true, ask again straight away. We refresh the data every 5 minutes.
since = load_cursor()
while True:
r = requests.get("https://parser.randomstar.org/api/v1/changes",
params={"since_id": since, "limit": 500},
headers={"Authorization": "Bearer " + KEY}, timeout=30).json()
for ch in r["data"]:
save(ch)
since = r["meta"]["next_since_id"]
save_cursor(since)
if not r["meta"]["has_more"]:
break
Webhooks
Instead of polling /changes, give us an address and we will push events to it: new lots for your filters and changes to your tracked lots (the same ones the Telegram bot sends). Set it in your account, on the API tab, where you also find the signing secret, a test button and the last delivery status. One webhook per account; the address must be public and https://.
Events are collected every 5 minutes and arrive in a batch — a POST with JSON: headers X-Radar-Event (lots, or ping for the test), X-Radar-Delivery, X-Radar-Timestamp, X-Radar-Signature; body {"id", "type", "created_at", "events": [...]}, where each event has type (lot.new for a new lot matching a filter, lot.changed for a tracked lot, lot.relisted when an asset you tracked is put up again — the new lot is added to tracking automatically), reason (filter, watch or relist), filter (id, name or null), change (id, event, old, new, at) and lot in the same shape as /lots. An event may arrive twice — deduplicate by change.id.
Answer with 2xx within 10 seconds. Otherwise we retry after 1, 5, 30 minutes, 2 and 6 hours, then drop the batch; events keep their order, so later ones wait. After 20 failures in a row the webhook is switched off — the reason is shown in your account, and “Save and test” switches it back on. Redirects are not followed.
The signature is HMAC-SHA256 of {X-Radar-Timestamp}.{request body} with your secret. Verify it and reject requests older than a few minutes:
$body = file_get_contents('php://input');
$ts = (int)($_SERVER['HTTP_X_RADAR_TIMESTAMP'] ?? 0);
$sig = 'sha256=' . hash_hmac('sha256', $ts . '.' . $body, getenv('RADAR_WEBHOOK_SECRET'));
if (!hash_equals($sig, $_SERVER['HTTP_X_RADAR_SIGNATURE'] ?? '') || abs(time() - $ts) > 300) {
http_response_code(401); exit;
}
http_response_code(200);
Errors
An error comes with an HTTP code and {"error": {"code": "…", "message": "…"}}: 400 bad parameter (bad_sort, bad_status, bad_date), 401 no or unknown key, 403 plan without API, 404 no such lot, filter or method, 405 not GET, 429 daily limit reached.
Terms
- The data is Prozorro.Sale open data; when showing lots to your users, link to the source (
source_url). - There is no personal data of private individuals in the API — we strip it at collection time.
- Usage rules and limits are in the public offer. Need a different limit? Contact us.
Haven’t found the answer?
Check the questions and answers or write to us — we will reply and fill the gap in the documentation.