Ingest a Report
Submit a publicly accessible intelligence report for automated extraction and ingestion. Babel fetches the URL, extracts entities and relationships using LLM analysis, enriches any indicators, and links the results into the existing knowledge graph.
All ingest endpoints require a key with the Intel Contributor role (or higher), and count against your monthly quota.
Submit a report
POST/ingestAuth required
Submit a report URL. The report itself is processed synchronously — the response is returned once it is saved.
| Parameter | Type | Description | |
|---|---|---|---|
url | string | req | Publicly accessible URL of the report or advisory |
Example request
bash
curl -X POST https://api.getbabel.io/ingest \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "url": "https://example.com/threat-report.pdf" }'Response shapes
The report itself is processed synchronously. Report links found inside it (up to 10) are queued and processed afterwards in the background, so they are not part of the response. The possible outcomes:
Success — new report processed:
json
{
"success": true,
"already_exists": false,
"primary": {
"report_id": "a3f2c8d1-...",
"title": "MuddyWater targets Israeli organisations",
"summary": {
"source": "Kaspersky",
"actors": 1,
"clusters": 0,
"campaigns": 0,
"tools": 2,
"ttps": 8,
"cves": 1,
"indicators": 12,
"edges": 34,
"historical_skipped": 0
}
},
"depth_queued": 4,
"depth1": [],
"depth2": [],
"already_existed": [],
"failed": []
}depth_queued is the number of linked report URLs queued for background processing. depth1, depth2, already_existed and failed are kept for backward compatibility and are always empty arrays.
Already ingested — URL was previously processed:
json
{
"success": true,
"already_exists": true,
"title": "MuddyWater targets Israeli organisations",
"url": "https://example.com/threat-report.pdf"
}Fetch failed — the URL could not be retrieved:
json
{
"success": false,
"fetch_failed": true,
"url": "https://example.com/threat-report.pdf",
"tried": ["https://example.com/threat-report.pdf"],
"error": "HTTP 403"
}Not a single report — the page is a listing or archive of several reports:
json
{
"success": false,
"catalog_rejected": true,
"url": "https://example.com/blog/"
}Errors: 429 when the monthly quota is reached or the same URL is already being processed for you; 503 with a Retry-After header when the server is busy with other reports.
Streaming ingestion
POST/ingest/streamAuth required
Same as /ingest but uses server-sent events to stream progress updates in real time. Useful for UI integrations.
Text ingestion
POST/ingest/textAuth required
Submit raw report text instead of a URL. Use when the target page is behind a paywall or login.
| Parameter | Type | Description | |
|---|---|---|---|
url | string | req | Canonical URL of the report (used for deduplication and provenance — the page does not need to be accessible) |
text | string | req | Full text content of the report (request body up to 5 MB) |
On success, returns { "success": true, "already_exists": false, "data": { ... } }, where data holds the extracted entities. Linked reports are not followed for text submissions.
