API

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.

PlanRequests per day
Pro1,000
Business20,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

RequestReturns
GET /api/v1/lotsLot search, paginated
GET /api/v1/lots/UA-…One lot: all fields, change history, other auctions of the same asset
GET /api/v1/changesChange feed (new lots, price, dates, status, documents, result) for syncing
GET /api/v1/filtersYour saved filters
GET /api/v1/filters/id/lotsLots for your filter — the same ones the bot sends you
GET /api/v1/meKey owner, plan, limit and usage today
WebhookWe push new lots and changes to your address
GET /api/v1/kinds, /regions, /statusesCode lists (no key needed)

Lot search: /lots

ParameterValue
qWords from the title or locality; a UA-… auction number or a cadastral number searches exactly
kindAuction type code from /kinds, e.g. land_lease
regionRegion as in /regions (Ukrainian), e.g. Полтавська
min, maxStarting price, UAH
dealdiscount — only discounted repeat auctions, dutch — only Dutch auctions (the price goes down)
statusopen (default — accepting or about to accept bids), all, or codes from /statuses, comma-separated
published_since, modified_sinceISO 8601 date or date-time
sortnew (default), modified, deadline, price_asc, price_desc
page, per_pagePage 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.