Making requests
The rules every request follows: where to send it, what to send with it, how lists come back in pages, and how times, dates and prices are written.
Every request goes to one of two base addresses, carries your API key, and sends and receives JSON. This page also covers lists that come back in pieces, sending a write safely a second time, caching, and how dates, times and prices are written. Developers need all of it. If you work in Excel or Power BI, the Excel recipe covers what you need.
Base URLs
| API | Base URL | Use it for |
|---|---|---|
| Rate Capture API | https://api.pro.getbeacon.cloud/v1 |
All of Rate Capture: credits, reports, runs, rows and rates. You can read and write. |
| Lighthouse-compatible API | https://api.pro.getbeacon.cloud/lighthouse/v3 |
Reading rates with a client already written for Lighthouse's v3 API. Read only. See Switching from Lighthouse. |
Paths in these docs start from the domain, such as GET /v1/runs or GET /lighthouse/v3/rates, so the full address of the
first is https://api.pro.getbeacon.cloud/v1/runs.
HTTPS and JSON
- HTTPS only.
-
Parameters go in the query string for
GET, and in a JSON body forPOST. Send the body withContent-Type: application/json. A body that isn't valid JSON, or isn't a JSON object, answers422 invalid_json. -
Every answer is JSON, with
Content-Type: application/json; charset=utf-8, unless you ask for another format: ndjson for a whole run, or CSV from the Lighthouse-compatible API. -
Answers aren't cached. They carry
Cache-Control: no-store, including the ndjson and CSV formats you ask for. The one exception is a200fromGET /v1/runs/{id}/rates, which carriesprivate, max-age=60. See caching rates.
curl -X POST https://api.pro.getbeacon.cloud/v1/runs \
-H "Authorization: Bearer $BEACON_KEY" \
-H "Idempotency-Key: client-42-2026-10-05" \
-H "Content-Type: application/json" \
-d '{"reference": "client-42", "name": "Harbour competitors", "currency": "GBP", "nights": 1,
"properties": [{"url": "https://www.booking.com/hotel/gb/harbour-view.html", "name": "Harbour View Hotel"}]}'
Paths that don't exist
An address under /v1 that doesn't exist answers 404 not_found in the usual error shape, never
an HTML page. So your code can read even this answer as JSON.
{"error": {"code": "not_found", "message": "No such API path."}}
Under /lighthouse/v3, a Lighthouse endpoint that Beacon doesn't provide answers 501 not_supported instead, so a Lighthouse
client finds out at once. Any other address under /lighthouse answers 404 endpoint_not_found. See
the error codes.
Authentication
Every request needs an API key, sent as Authorization: Bearer followed by the key. Lighthouse clients already send their key in a header
called X-Oi-Authorization, and Beacon accepts that too. Three kinds of answer need no key: the Rate Capture API's OpenAPI file at
/v1/openapi.json, unknown paths, and the Lighthouse 501 not_supported answers.
How to create a key, and what each one may do, is in API keys.
Lists and pages
The Rate Capture API's three lists come back a page at a time. Ask for a page size with limit. Each answer has a
nextCursor: pass it back unchanged as cursor to get the next page. On the last page it is null.
| List | limit |
Order |
|---|---|---|
GET /v1/reports |
1 to 200, default 50 | Newest first |
GET /v1/runs |
1 to 200, default 50 | Newest first |
GET /v1/runs/{id}/results |
1 to 2000, default 500 | Check-in date, then property, room and price |
-
A cursor is a bookmark, not something to read. Don't build or change one. A cursor from another list, or another run, answers
422 invalid_cursor. - A
limitthat isn't a whole number in range answers422 invalid_limit.
curl -H "Authorization: Bearer $BEACON_KEY" \
"https://api.pro.getbeacon.cloud/v1/runs/1204/results?limit=500"
# then, with the nextCursor from that answer
curl -H "Authorization: Bearer $BEACON_KEY" \
"https://api.pro.getbeacon.cloud/v1/runs/1204/results?limit=500&cursor=$NEXT_CURSOR"
A whole run in one request
GET /v1/runs/{id}/results?format=ndjson sends every row of the run in one answer, with no pages. It uses
Content-Type: application/x-ndjson: one JSON object on each line. The first line is the run, with "kind": "run" in front,
and every line after it is one row. limit is ignored. This is the cheapest way to take a whole run.
curl -H "Authorization: Bearer $BEACON_KEY" \ "https://api.pro.getbeacon.cloud/v1/runs/1204/results?format=ndjson" > run-1204.ndjson
Pages in the Lighthouse-compatible API
/lighthouse/v3/hotels is paged the Lighthouse way instead. Without page it returns every subscription, with no
meta. With page (1 or more) it returns that page of per_page subscriptions (1 to 100, default 100), and the
answer's meta gives page, per_page, total_pages and total_results. A value out of
range answers 400, and so does a page past the last one.
Sending a write again
Send an Idempotency-Key header on a write, so that sending it again after a timeout never creates or charges twice. It works on
POST /v1/reports, POST /v1/reports/{id}/runs, POST /v1/runs, POST /v1/runs/{id}/retry and
DELETE /v1/runs/{id}/results.
-
One key for each thing you mean to do once, up to 200 characters, chosen by you. A longer key answers
422 invalid_idempotency_key. Keys belong to your company. -
Same key, path and body: Beacon answers exactly what it answered the first time, and does nothing again. The same key with
another path or body is
422 idempotency_mismatch. - Keys are forgotten after 24 hours. After that, the same key starts a new action.
-
If the first request is still being handled, the answer is
409 idempotency_in_progress. Wait the 5 seconds inRetry-Afterand send it again with the same key. A first request that never finished is given up after 60 seconds, and the key can then be used again. -
Answers that are kept: every
2xx,402 insufficient_creditsand409 report_already_run. Sending the same key again returns the same answer, so fix the cause and use a new key. -
Answers that aren't kept:
429,422,500, and a409about the run's state (run_not_completed,retry_cooldownorresults_purged). Fix the cause, then send the same key again.
{"error": {"code": "idempotency_mismatch", "message": "This Idempotency-Key was already used for a different request (another endpoint or another body)."}}
Caching rates
A 200 from GET /v1/runs/{id}/rates carries three headers: ETag,
Cache-Control: private, max-age=60 and Vary: Authorization, X-Oi-Authorization. Your own client may reuse the answer for
60 seconds. A shared cache never may.
- Keep the
ETagvalue, exactly as sent, with the rates you stored. - Next time, send it back in an
If-None-Matchheader. -
If the run, its report's properties and your filters haven't changed, the answer is
304 Not Modified, with the same three headers and no body. Keep the rates you have.
curl -H "Authorization: Bearer $BEACON_KEY" \ -H "If-None-Match: $ETAG_FROM_LAST_ANSWER" \ "https://api.pro.getbeacon.cloud/v1/runs/1204/rates?persons=2"
A retry of failed checks, a change to the report's properties, different filters or a change to Beacon's answer format all give a new tag and a full
200.
The tags are compared loosely. A W/ in front or -gzip on the end still matches, and If-None-Match: * matches any tag.
Errors are never cached and never answer 304.
Only GET /v1/runs/{id}/rates sends an ETag. The Lighthouse-compatible endpoints don't: they answer
Cache-Control: no-store and always send the full answer.
Times, dates, prices and null
-
Times are in UTC, written like
2026-10-05T08:00:12Z. The one exception isextractDateTimein the Lighthouse-compatible API, which follows Lighthouse and has noZ, but is also UTC. - Dates are written
YYYY-MM-DD. -
Prices are numbers for the whole stay in the report's currency. In JSON, prices always have a decimal point, such as
99.0. In the Lighthouse-compatible CSV, a whole price has none, such as99. Whether a price includes tax is not captured. - Beacon's ids are whole numbers.
roomIdandrateIdare the site's own ids and are text. -
A field with no value is
null. It is never missing from the answer. The exception isdetailsin an error, which appears only when there is more to say. In CSV,nullis an empty cell.