Machine-readable: openapi.json (OpenAPI 3.1) · llms.txt

outpost API

Run HTTP and TCP probes from a fleet of workers with fixed egress IPs, selected by labels, with separate DNS/connect/TLS/TTFB timings per worker. Base URL: https://outpost.neubauer-digital.at. Machine-readable spec: /openapi.json (OpenAPI 3.1).

Authentication

Send Authorization: Bearer <token> on every /v1 request. Tokens look like opk_… and are created by an admin at /admin/. Roles, each including the ones before it:

GET /v1/whoami tells you your role. 401 = token missing, invalid, expired or revoked. 403 = role too low. Never put tokens in URLs or logs.

Core workflow: measure a URL

  1. POST /v1/checks?wait=10s with a CheckRequest.
  2. 200 means finished (status is done, partial or failed). 202 means still running: poll GET /v1/checks/{id} every 1–2 s until status is terminal.
  3. Read results[]: one entry per worker, each with status (job) and result (measurement).
POST /v1/checks?wait=10s
Authorization: Bearer opk_...
Content-Type: application/json

{
  "type": "http_get",
  "http": {"url": "https://example.com/"},
  "agents": {"workload": "monitoring", "count": 2},
  "ip_version": "auto",
  "timeout_ms": 10000
}

TCP connect with TLS handshake, IPv4 only:

{"type": "tcp_connect", "tcp": {"host": "mail.example.com", "port": 443, "tls": true},
 "agents": {"workload": "monitoring", "count": 2}, "ip_version": "ipv4"}

If your proxy blocks POST

GET /v1/run does the same thing with query parameters and returns the same response:

GET /v1/run?type=http_get&url=https%3A%2F%2Fexample.com%2F&workload=scraping&return_body=true&wait=20s&include_body=true
Authorization: Bearer opk_...

Parameters: type, workload, url (URL-encoded), host, port, tls, server_name, follow_redirects, max_redirects, insecure_tls, return_body, count, ip_version, timeout_ms, max_bytes, label.<key>=, header.<Name>= (e.g. header.Accept-Language=de-AT), wait, include_body. Unknown parameters get 400. Keep the token in the Authorization header, never in the URL.

Fetching page content (HTML)

Set "return_body": true in http and use a scraping agent:

POST /v1/checks?wait=20s&include_body=true
{"type": "http_get",
 "http": {"url": "https://example.com/page", "return_body": true, "follow_redirects": true},
 "agents": {"workload": "scraping", "count": 1},
 "max_bytes": 4194304, "timeout_ms": 15000}

Request headers and User-Agent

http.headers is sent as given, including User-Agent, Accept and Accept-Language. Without one, the User-Agent is outpost/1 (+https://outpost.neubauer-digital.at/docs), which identifies the service to site operators. Some shops block recognisable bots; outpost does not rotate agents, IPs or fingerprints and does not solve challenges. If a site blocks you (403/429), respect it rather than working around it.

CheckRequest fields

Unknown fields are rejected with 400.

Reading results

Check status:

Per-worker results[].status: done, rejected (see error), lost (no answer within timeout + 15 s), queued, dispatched or running.

Measurement results[].result:

Compare workers by results[].hostname (a label) or agent_id. Egress IPs are in the egress_ip label from GET /v1/agents.

Workloads (important)

Each worker has exactly one workload. If fewer workers match than count, the API returns 422 with {error, workload, eligible, requested} and creates nothing. Jobs are never rerouted to the other workload. Lower count or relax labels; do not switch workload just to make a request succeed. Check GET /v1/agents (online, labels) to see what is available.

Rate limits

Dispatch is rate limited centrally per registrable domain (for example a.example.com and b.example.com share one bucket) across all workers: default 1 request/s, burst 5. Excess jobs wait in queued for up to 10 min. HTTP 429/503 from a target backs off the whole domain exponentially. Checks are accepted anyway, only dispatch waits. So batch sensibly and expect queued under load.

Other endpoints

Errors

JSON {"error": "..."} with 400 (validation; fix the request, do not retry unchanged), 401, 403, 404, 409, 422 (not enough agents), 500 (retry with backoff).

Links