API documentation
Submit reports, search indicators and export results from your own tools
Authentication
Authenticated endpoints use API tokens for programmatic access. Most read endpoints are public and require no authentication.
API tokens
Create a personal API token from Settings. Tokens are prefixed with ioc_ and are shown only once on creation.
Include your token as a Bearer token in the Authorization header:
import requests
API_BASE = "https://iocget.com/api"
API_TOKEN = "ioc_your_token_here"
headers = {
"Authorization": f"Bearer {API_TOKEN}"
}
response = requests.get(f"{API_BASE}/auth/me", headers=headers)
print(response.json())
const API_BASE = "https://iocget.com/api";
const API_TOKEN = "ioc_your_token_here";
const headers = {
"Authorization": `Bearer ${API_TOKEN}`,
"Content-Type": "application/json"
};
const response = await fetch(`${API_BASE}/auth/me`, { headers });
const data = await response.json();
console.log(data);
curl https://iocget.com/api/auth/me \
-H "Authorization: Bearer ioc_your_token_here"
Submissions
Submissions represent documents or URLs submitted for IOC extraction. Requires authentication to create; read endpoints are public.
Submit a file
/api/submissions/file
Auth required
Upload a file for IOC extraction. Supported types: TXT, HTML, PDF, DOCX, DOC. Max 50 MB.
| Field | Type | Description |
|---|---|---|
file | multipart/form-data | The file to extract IOCs from |
import requests
headers = {"Authorization": "Bearer ioc_your_token_here"}
with open("threat_report.pdf", "rb") as f:
response = requests.post(
"https://iocget.com/api/submissions/file",
headers=headers,
files={"file": ("threat_report.pdf", f, "application/pdf")}
)
submission = response.json()
print(f"Submission ID: {submission['id']}, Status: {submission['status']}")
const formData = new FormData();
formData.append("file", fileInput.files[0]);
const response = await fetch("https://iocget.com/api/submissions/file", {
method: "POST",
headers: { "Authorization": "Bearer ioc_your_token_here" },
body: formData
});
const submission = await response.json();
console.log(`Submission ID: ${submission.id}, Status: ${submission.status}`);
Response
{
"id": "sub_abc123",
"status": "pending",
"source_type": "file_upload",
"original_filename": "threat_report.pdf",
"created_at": "2024-01-15T14:30:00Z"
}
GET /api/submissions/{id} until status is completed or failed. If the file was already submitted, the response includes "duplicate": true with the existing submission ID.Submit a URL
/api/submissions/url
Auth required
Submit a URL for IOC extraction. The content is fetched and analyzed automatically.
| Field | Type | Description |
|---|---|---|
url | string | The URL to fetch and extract IOCs from |
import requests
import time
headers = {
"Authorization": "Bearer ioc_your_token_here",
"Content-Type": "application/json"
}
# Submit URL
response = requests.post(
"https://iocget.com/api/submissions/url",
headers=headers,
json={"url": "https://example.com/threat-report"}
)
submission = response.json()
submission_id = submission["id"]
# Poll for completion
while True:
result = requests.get(
f"https://iocget.com/api/submissions/{submission_id}",
headers=headers
).json()
if result["status"] in ("completed", "failed"):
break
time.sleep(5)
print(f"Found {result['ioc_count']} IOCs")
const headers = {
"Authorization": "Bearer ioc_your_token_here",
"Content-Type": "application/json"
};
// Submit URL
const response = await fetch("https://iocget.com/api/submissions/url", {
method: "POST",
headers,
body: JSON.stringify({ url: "https://example.com/threat-report" })
});
const { id } = await response.json();
// Poll for completion
async function waitForCompletion(submissionId) {
while (true) {
const result = await fetch(
`https://iocget.com/api/submissions/${submissionId}`,
{ headers }
).then(r => r.json());
if (result.status === "completed" || result.status === "failed") {
return result;
}
await new Promise(r => setTimeout(r, 5000));
}
}
const result = await waitForCompletion(id);
console.log(`Found ${result.ioc_count} IOCs`);
List submissions
/api/submissions
Public
List recent submissions, ordered by creation date descending. Supports incremental sync via the since parameter.
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 50 | Number of results (max 100) |
offset | integer | 0 | Pagination offset |
since | string | — | ISO 8601 UTC timestamp. Returns only submissions created or updated after it, oldest first. See incremental sync. |
import requests
response = requests.get(
"https://iocget.com/api/submissions",
params={"limit": 20, "offset": 0}
)
submissions = response.json()
for sub in submissions:
print(f"{sub['id']}: {sub['title'] or sub['source_identifier']} ({sub['ioc_count']} IOCs)")
const response = await fetch(
"https://iocget.com/api/submissions?limit=20&offset=0"
);
const submissions = await response.json();
for (const sub of submissions) {
console.log(`${sub.id}: ${sub.title || sub.source_identifier} (${sub.ioc_count} IOCs)`);
}
Incremental sync
Use the since parameter to maintain a local mirror of all submissions without re-fetching the entire table on each run. When since is provided, the endpoint filters on updated_at and returns results oldest-first, so you capture both new submissions and changes to existing ones (status transitions, admin edits, etc.). Start from 1970-01-01T00:00:00Z, then pass the updated_at of the last record you received as since on the next call.
import requests
API_BASE = "https://iocget.com/api"
def sync_submissions(cursor: str | None = None) -> tuple[list, str | None]:
"""Fetch submissions updated after `cursor`. Returns (submissions, next_cursor)."""
params = {"limit": 100}
if cursor:
params["since"] = cursor
response = requests.get(f"{API_BASE}/submissions", params=params)
response.raise_for_status()
submissions = response.json()
next_cursor = submissions[-1]["updated_at"] if submissions else cursor
return submissions, next_cursor
# Initial full sync: start at the beginning and walk forward
all_submissions = []
cursor = "1970-01-01T00:00:00Z"
while True:
batch, cursor = sync_submissions(cursor)
if not batch:
break
all_submissions.extend(batch)
if len(batch) < 100:
break # No more pages
# Later: incremental sync — pick up new and updated submissions
new_batch, cursor = sync_submissions(cursor)
print(f"Found {len(new_batch)} new/updated submissions")
const API_BASE = "https://iocget.com/api";
async function syncSubmissions(cursor = null) {
const url = new URL(`${API_BASE}/submissions`);
url.searchParams.set("limit", "100");
if (cursor) url.searchParams.set("since", cursor);
const response = await fetch(url);
const submissions = await response.json();
const nextCursor = submissions.length
? submissions[submissions.length - 1].updated_at
: cursor;
return { submissions, nextCursor };
}
// Initial full sync: start at the beginning and walk forward
let cursor = "1970-01-01T00:00:00Z";
const allSubmissions = [];
while (true) {
const { submissions, nextCursor } = await syncSubmissions(cursor);
cursor = nextCursor;
if (!submissions.length) break;
allSubmissions.push(...submissions);
if (submissions.length < 100) break; // no more pages
}
// Later: incremental sync — pick up new and updated submissions
const { submissions: newBatch, nextCursor } = await syncSubmissions(cursor);
console.log(`Found ${newBatch.length} new/updated submissions`);
For filtered search, use GET /api/submissions/search with optional q, status, and source_type query parameters.
Get a submission
/api/submissions/{submission_id}
Public
Get full details for a submission, including all extracted IOCs.
import requests
submission_id = "sub_abc123"
response = requests.get(f"https://iocget.com/api/submissions/{submission_id}")
data = response.json()
print(f"Title: {data['title']}")
print(f"Status: {data['status']}")
print(f"IOC count: {data['ioc_count']}")
for ioc in data["iocs"]:
print(f" [{ioc['ioc_type']['name']}] {ioc['value']}")
const submissionId = "sub_abc123";
const response = await fetch(
`https://iocget.com/api/submissions/${submissionId}`
);
const data = await response.json();
console.log(`Title: ${data.title}`);
console.log(`IOC count: ${data.ioc_count}`);
for (const ioc of data.iocs) {
console.log(` [${ioc.ioc_type.name}] ${ioc.value}`);
}
Response
{
"id": "sub_abc123",
"title": "Threat Report Q1 2024",
"summary": "Analysis of APT29 campaign targeting...",
"status": "completed",
"source_type": "file_upload",
"original_filename": "threat_report.pdf",
"ioc_count": 42,
"iocs": [
{
"id": "ioc_xyz789",
"value": "192.0.2.1",
"refanged_value": "192[.]0[.]2[.]1",
"ioc_type": { "name": "ipv4" },
"context": "C2 server observed in network logs",
"description": "C2 server",
"created_at": "2024-01-15T14:30:00Z"
}
],
"created_at": "2024-01-15T14:28:00Z",
"processed_at": "2024-01-15T14:30:00Z"
}
Export a submission
/api/submissions/{submission_id}/export
Public
Export a completed submission in various formats.
| Parameter | Values | Description |
|---|---|---|
format | json, csv, minimal (CSV of type and refanged value only), stix, ecs, misp | Export format (default: json) |
import requests
submission_id = "sub_abc123"
# Export as STIX 2.1
response = requests.get(
f"https://iocget.com/api/submissions/{submission_id}/export",
params={"format": "stix"}
)
with open("report.stix.json", "wb") as f:
f.write(response.content)
# Export as CSV
response = requests.get(
f"https://iocget.com/api/submissions/{submission_id}/export",
params={"format": "csv"}
)
print(response.text)
const submissionId = "sub_abc123";
// Export as CSV and trigger download
const response = await fetch(
`https://iocget.com/api/submissions/${submissionId}/export?format=csv`
);
const blob = await response.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "ioc_report.csv";
a.click();
Generate Sigma rules
/api/submissions/{submission_id}/sigma
Auth required
Generate Sigma detection rules for a completed submission. Rules are built from the submission's IOCs and the original article, grouped into one rule per log source. They are AI-generated with status experimental: review and test them before deploying.
Generation usually takes 15–40 seconds and runs as a job. Pass wait to get the rules back in one call: the response is 200 with the finished job if it completes within wait seconds, otherwise 202 with a job to poll. Without wait the response is always 202. Results are kept for one hour. Limited to 10 requests per hour.
| Parameter | Type | Default | Description |
|---|---|---|---|
wait | integer | 0 | Seconds to wait for the rules before returning a job to poll (max 90) |
import requests
import time
headers = {"Authorization": "Bearer ioc_your_token_here"}
base = "https://iocget.com/api/submissions/sub_abc123/sigma"
job = requests.post(base, headers=headers, params={"wait": 60}).json()
# Still running after the wait: poll until it finishes
while job["status"] in ("pending", "processing"):
time.sleep(3)
job = requests.get(f"{base}/{job['job_id']}", headers=headers).json()
if job["status"] == "failed":
raise RuntimeError(job["error"])
for rule in job["result"]["rules"]:
print(f"{rule['title']} ({rule['level']})")
# All rules as one multi-document YAML file
with open("sigma_rules.yml", "w") as f:
f.write(job["result"]["yaml"])
const headers = { Authorization: "Bearer ioc_your_token_here" };
const base = "https://iocget.com/api/submissions/sub_abc123/sigma";
let job = await (await fetch(`${base}?wait=60`, { method: "POST", headers })).json();
// Still running after the wait: poll until it finishes
while (job.status === "pending" || job.status === "processing") {
await new Promise(resolve => setTimeout(resolve, 3000));
job = await (await fetch(`${base}/${job.job_id}`, { headers })).json();
}
if (job.status === "failed") throw new Error(job.error);
for (const rule of job.result.rules) {
console.log(`${rule.title} (${rule.level})`);
}
Response
{
"job_id": "3f9c2a7e1b4d4c6f8e0a5b7d9c1e2f34",
"status": "completed",
"message": null,
"elapsed_seconds": 18,
"result": {
"submission_id": "sub_abc123",
"article_included": true,
"rules": [
{
"title": "C2Looper C2 Network Connection",
"level": "critical",
"logsource": { "category": "network_connection", "product": "windows" },
"yaml": "title: C2Looper C2 Network Connection\nid: d6ba803c-...\nstatus: experimental\n..."
}
],
"yaml": "title: C2Looper C2 Network Connection\n...\n---\ntitle: ..."
}
}
status is one of pending, processing, completed or failed. result is present once completed and error once failed. article_included is false when the original article could not be retrieved (for example for file uploads); the rules are then based on the IOCs and summary only. If a job for the same submission is already running, the request returns that job instead of starting another.
Poll a Sigma job
/api/submissions/{submission_id}/sigma/{job_id}
Auth required
Poll a Sigma generation job. Returns the same job object as above. Jobs are only visible to the user who started them and are kept for one hour; after that this returns 404.
IOCs
IOCs are deduplicated by value and type across all submissions. A single IOC can appear in multiple reports.
Supported IOC types
Search IOCs
/api/iocs/search
Public
Search across all IOCs with optional filtering by value or type.
| Parameter | Type | Default | Description |
|---|---|---|---|
q | string | — | Search query (partial match on value) |
type | string | — | Filter by IOC type (e.g. ipv4, domain) |
limit | integer | 50 | Number of results (max 100) |
offset | integer | 0 | Pagination offset |
import requests
# Search for all IPv4 IOCs
response = requests.get(
"https://iocget.com/api/iocs/search",
params={"type": "ipv4", "limit": 100}
)
data = response.json()
print(f"Found {data['total']} IPv4 IOCs")
# Search by value
response = requests.get(
"https://iocget.com/api/iocs/search",
params={"q": "192.168", "type": "ipv4"}
)
for ioc in response.json()["iocs"]:
print(f"{ioc['value']} — seen in {ioc['submission_count']} reports")
// Search for all domain IOCs
const response = await fetch(
"https://iocget.com/api/iocs/search?type=domain&limit=50"
);
const data = await response.json();
console.log(`Found ${data.total} domain IOCs`);
// Search by value with pagination
async function* searchIOCs(query, type) {
let offset = 0;
while (true) {
const params = new URLSearchParams({ q: query, type, limit: 100, offset });
const res = await fetch(`https://iocget.com/api/iocs/search?${params}`);
const page = await res.json();
yield* page.iocs;
if (offset + page.iocs.length >= page.total) break;
offset += 100;
}
}
for await (const ioc of searchIOCs("malware", "domain")) {
console.log(ioc.value);
}
Response
{
"total": 1240,
"limit": 50,
"offset": 0,
"iocs": [
{
"id": "ioc_xyz789",
"value": "malicious-domain.example.com",
"refanged_value": "malicious-domain[.]example[.]com",
"type": "domain",
"context": "Observed in phishing campaign targeting...",
"submission_id": "sub_abc123",
"submission_count": 3,
"created_at": "2024-01-15T14:30:00Z"
}
]
}
Get an IOC
/api/iocs/{ioc_id}
Public
Get full details for a specific IOC, including all submissions where it appears.
import requests
ioc_id = "ioc_xyz789"
response = requests.get(f"https://iocget.com/api/iocs/{ioc_id}")
ioc = response.json()
print(f"Value: {ioc['value']}")
print(f"Type: {ioc['ioc_type']['name']}")
print(f"First seen: {ioc['first_seen_at']}")
print(f"Seen in {ioc['submission_count']} reports:")
for sub in ioc["submissions"]:
print(f" - {sub['title'] or sub['source_identifier']}")
const iocId = "ioc_xyz789";
const response = await fetch(`https://iocget.com/api/iocs/${iocId}`);
const ioc = await response.json();
console.log(`Value: ${ioc.value}`);
console.log(`Type: ${ioc.ioc_type.name}`);
console.log(`Seen in ${ioc.submission_count} reports:`);
for (const sub of ioc.submissions) {
console.log(` - ${sub.title || sub.source_identifier}`);
}
Stats
/api/iocs/stats
Public
Get aggregate statistics: total IOC and submission counts, broken down by IOC type.
import requests
response = requests.get("https://iocget.com/api/iocs/stats")
stats = response.json()
print(f"Total IOCs: {stats['total_iocs']}")
print(f"Total reports: {stats['total_submissions']}")
print("\nBreakdown by type:")
for ioc_type, count in sorted(stats["iocs_by_type"].items(), key=lambda x: -x[1]):
print(f" {ioc_type}: {count}")
const response = await fetch("https://iocget.com/api/iocs/stats");
const stats = await response.json();
console.log(`Total IOCs: ${stats.total_iocs}`);
console.log(`Total reports: ${stats.total_submissions}`);
const sorted = Object.entries(stats.iocs_by_type)
.sort(([, a], [, b]) => b - a);
for (const [type, count] of sorted) {
console.log(` ${type}: ${count}`);
}
Response
{
"total_iocs": 15820,
"total_submissions": 342,
"iocs_by_type": {
"ipv4": 4210,
"domain": 3890,
"hash_sha256": 2540,
"url": 1980,
"hash_md5": 1430,
...
}
}