| File | Date | Author | Commit |
|---|---|---|---|
| examples | 3 days ago |
|
[428ada] Add Postman collection + environment |
| .gitignore | 3 days ago |
|
[5188ca] Initial library: README + examples in 7 languages |
| LICENSE | 3 days ago |
|
[5188ca] Initial library: README + examples in 7 languages |
| README.md | 3 days ago |
|
[428ada] Add Postman collection + environment |
Real-world examples for using the oruk live broadcast intelligence API in PHP, Python, JavaScript, TypeScript, Ruby, Go, and curl.
oruk listens to ~200 radio, TV, social, and structured feeds in real time and publishes corroborated news events with stable ids, multi-source verification, location, and confidence scores. The same data powers the live wire at oruk.ai.
https://api.oruk.ai# Public feed - no key required
curl https://api.oruk.ai/v1/stories/feed?limit=10
# Authed feed with filters
curl -H "X-API-Key: ork_xxxxxxxx" \
"https://api.oruk.ai/v1/stories?category=conflict&min_impact=7&limit=20"
That's the whole API in two requests. Everything below is variations on this theme.
Pass your API key on every authed request. Three accepted formats:
| Method | Header / param |
|---|---|
| Preferred | X-API-Key: ork_xxxx |
| OAuth-style | Authorization: Bearer ork_xxxx |
| EventSource fallback | ?api_key=ork_xxxx (SSE only - for browsers that can't set headers) |
Public endpoints (no key required, no quota counted): GET /health, GET /v1/health, GET /v1/stories/feed.
Everything else under /v1/* needs a key. Get one at https://oruk.ai/dashboard.
| Method | Path | Auth | Purpose |
|---|---|---|---|
| GET | /health |
public | Liveness probe |
| GET | /v1/stories/feed |
public | Pre-built feed of latest stories |
| GET | /v1/stories |
key | Filterable + paginated story list |
| GET | /v1/stories/{id} |
key | Single story by evt_… id with full timeline and source quotes |
| GET | /v1/stream |
key + Trader/Dev/Enterprise | Server-Sent Events stream |
| GET | /v1/sources |
key | Every monitored source (station, country, language, medium) |
| GET | /v1/regions |
key | Regional story-count aggregates |
| GET | /v1/stats |
key | System stats (active sources, throughput, top categories) |
| POST | /v1/webhooks |
Developer+ | Subscribe an HTTPS endpoint to story / corroboration events |
/v1/stories| Param | Type | Notes |
|---|---|---|
limit |
int 1-100 | default 20 |
cursor |
string | story id from a prior page |
category |
string | one of the 12 categories - see below |
since |
ISO 8601 | 2026-04-28 or 2026-04-28T15:00:00Z |
topics |
csv | topic filter (intersection) |
q |
string | full-text across headline / summary / body / source / city |
region |
string | one of 6 regions (North America, South America, Europe, Middle East, Africa, Asia-Pacific) |
country |
string | ISO 3166-1 alpha-2 (US, DE, JP) |
urgency |
enum | breaking / developing / routine |
min_impact |
int 0-10 | |
min_confidence |
float 0.0-1.0 | |
format |
enum | json (default) / csv / jsonl |
Every story payload - from /v1/stories, /v1/stories/feed, /v1/stories/{id}, or the SSE stream - looks like this:
{
"id": "evt_8f3a2b",
"headline": "...",
"summary": "...",
"body": "...",
"category": "conflict",
"categories": ["conflict", "diplomacy"],
"topics": ["Iran", "aircraft", "military"],
"urgency": "breaking",
"impact": 9,
"confidence": 0.96,
"sourceName": "BBC World Service",
"sourceId": 14,
"eventCity": "London",
"eventCountry": "GB",
"eventRegion": "Europe",
"eventLat": 51.51,
"eventLon": -0.13,
"language": "en",
"firstSeenAt": "2026-04-28T22:13:42Z",
"updatedAt": "2026-04-28T22:14:08Z",
"timestamp": "2026-04-28T22:13:42Z",
"storyStatus": "developing",
"corroboration": {
"count": 4,
"sources": ["BBC", "NPR", "Al Araby", "France Info"],
"sourceDetails": [{"name": "BBC", "region": "Europe", "language": "en", "medium": "audio_radio"}]
},
"timeline": [
{"at": "2026-04-28T21:30:00Z", "text": "Initial report on BBC World Service"},
{"at": "2026-04-28T21:42:00Z", "text": "NPR confirms with named official"}
],
"sources": [
{"station": "BBC World Service", "quote": "...", "medium": "audio_radio"}
]
}
Field semantics worth knowing:
corroboration.count is independent sources, not raw mentions. Two AP wires republished by different outlets count once.eventCity / eventCountry / eventRegion are where the news happened - don't confuse with sources[*].medium (where the broadcaster sits).confidence is the LLM extractor's self-reported confidence. Use >= 0.85 for high-confidence reads, >= 0.95 for automated decisioning.medium is one of audio_radio, social, structured.A story has exactly one primary category plus a multi-category categories[] array:
politics, conflict, economy, disaster, diplomacy, science, health, technology, culture, environment, sports, other.
Every error response has the same shape:
{"error": "<machine_code>", "message": "<human_message>"}
| HTTP | code | Meaning |
|---|---|---|
| 400 | invalid_request |
Malformed query parameters |
| 401 | unauthorized |
Missing or invalid API key |
| 404 | not_found |
Unknown story id |
| 429 | rate_limit_exceeded |
Monthly quota exhausted - honor Retry-After |
| 500 | internal_error |
Backend hiccup - retry with exponential backoff |
| 503 | service_unavailable |
Pipeline temporarily down (rare) |
Every response carries x-request-id. Include it in support tickets.
| Tier | $/mo | Calls/mo | API delay | Per-min | SSE |
|---|---|---|---|---|---|
| free | $0 | 100 | 5 min | 30 | – |
| pro | $12 | 1,000 | none | 60 | – |
| trader | $49 first month then $99 | 10,000 | none | 300 | ✓ |
| developer | $100 | 10,000 | none | 300 | ✓ |
| enterprise | contact | 1M+ | none | custom | ✓ |
Retry-After. Monthly quotas reset on the 1st of each month.Trader, Developer, and Enterprise tiers can connect to GET /v1/stream for Server-Sent Events.
Event types:
| Event | Payload |
|---|---|
story |
Full story payload (new or updated) |
corroboration |
{storyId, newSource, count, sources, ...} when an existing story gets a new corroborator |
heartbeat |
{activeSources: 218} system pulse (every ~15-30s) |
Reconnect with the Last-Event-ID header to resume from where you left off.
POST /v1/webhooks with:
{
"url": "https://your-app.com/hooks/oruk",
"events": ["story", "corroboration"],
"filters": {
"categories": ["conflict", "disaster"],
"min_impact": 7,
"min_confidence": 0.85,
"country": "US",
"topic_match": "earthquake"
}
}
Every payload is HMAC-SHA256 signed with your webhook secret in the X-Oruk-Signature header. See examples/php/webhook_verify.php, examples/python/webhook_verify.py, and examples/javascript/webhook_verify.js for verification snippets.
Up to 5 active webhooks per account.
Each subfolder has runnable code with a tiny README. Set ORUK_API_KEY in your environment first (export ORUK_API_KEY=ork_xxxxxxxx).
| Language / tool | Folder |
|---|---|
| curl | examples/curl/ |
| PHP | examples/php/ |
| Python | examples/python/ |
| JavaScript (Node) | examples/javascript/ |
| TypeScript | examples/typescript/ |
| Ruby | examples/ruby/ |
| Go | examples/go/ |
| Postman collection | examples/postman/ |
Every example covers four things consistently:
GET /v1/stories/{id} with parsed corroboration/v1/stream and handle story / corroboration / heartbeat eventsAlready using an LLM agent or IDE? The oruk-mcp package gives Claude Desktop, Cursor, Continue.dev, etc. native access to the oruk API as a Model Context Protocol server.
npx -y oruk-mcp
Configure in Claude Desktop / Cursor:
{
"mcpServers": {
"oruk": {
"command": "npx",
"args": ["-y", "oruk-mcp"],
"env": { "ORUK_API_KEY": "ork_xxxxxxxxxxxx" }
}
}
}
Without a key, the MCP runs in public mode and serves the freshest 50 stories from /v1/stories/feed. With a key, the full /v1/stories surface is available.
Building an autonomous agent? oruk publishes machine-readable discovery files:
https://oruk.ai/.well-known/ai.json - full capability manifesthttps://oruk.ai/.well-known/agent.json - lightweight skill cardhttps://oruk.ai/AGENTS.md - operating guide with citation ruleshttps://oruk.ai/llms.txt - curated LLM indexhttps://oruk.ai/llms-full.txt - exhaustive LLM contextMIT - see LICENSE. Use these snippets however you like.