skopio
← Back to pricing

Skopio API

B2B OSINT engine over a simple REST interface. Bearer auth, JSON in/out, flat-rate per contract.

Authentication

All endpoints require Authorization: Bearer skopio_live_xxx. Keys are issued manually after contract signing. Stored as SHA-256 hash; plaintext shown once at mint time.

Authorization: Bearer skopio_live_arEvkb...JU1M

Endpoints

POST/api/v1/scan

Creates a request and queues it in the engine. ETA is typically 60-180 seconds.

curl -X POST https://skopio.io/api/v1/scan \
  -H "Authorization: Bearer skopio_live_..." \
  -H "Content-Type: application/json" \
  -d '{"target":"john@example.com","kind":"email"}'

# Response 202
{
  "ok": true,
  "scan_id": "req-mp9ne00k-14242242",
  "kind": "email",
  "recipes_to_run": 6,
  "eta_seconds": 180,
  "poll_url": "https://skopio.io/api/v1/scan/req-mp9ne00k-14242242"
}
GET/api/v1/scan/{id}

Poll every 10-15 seconds. When status=completed the payload is ready.

curl https://skopio.io/api/v1/scan/req-mp9ne00k-14242242 \
  -H "Authorization: Bearer skopio_live_..."

# Response (status=completed)
{
  "ok": true,
  "scan_id": "req-...",
  "status": "completed",
  "target": "john@example.com",
  "kind": "email",
  "completed_at": "2026-05-17T12:34:56Z",
  "result": {
    "risk": {"score": 65, "band": "medium"},
    "counts": {"cases": 6, "entities": 192, "findings": 187},
    "profiles": {"verified": [...], "weak": [...]},
    "contacts":  {"emails": [...], "phones": [...]},
    "breaches":  [{"name":"LinkedIn","date":"2016-05","accounts":117000000,"classes":["Email","Password"]}],
    "phone_profile": null,
    "ai_summary": "Email фигурирует в 3 утечках...",
    "artifacts": {"pdf": "...", "person_card": "..."}
  }
}

Supported target types

email
john@example.com
phone
+15551234567
username
johndoe

Rate limits by tier

TierScans / day/ hourInflightBest for
Pro 100 20 3 Pro — solo integrations, freelance security, journalists.
Business 1,000 100 10 Business — agencies, HR background checks, due diligence, threat intel.
Enterprise10,00050025 Enterprise — fraud detection, SOC, monitoring large customer bases. SLA + dedicated support.

Tiers control THROUGHPUT only. Every API call deducts 1 credit from your account balance, regardless of tier. Exceeding rate-limit returns 429 with no deduction. Balance < 1 → 402 insufficient_balance.

Error codes

HTTPerrorMeaning
401unauthorized Missing or malformed Bearer header
401invalid_token Token not found or revoked
400target_too_short target must be ≥ 3 chars
429rate_limit_inflightToo many concurrent scans
429rate_limit_hour Hourly quota exceeded
429rate_limit_day Daily quota exceeded
403forbidden Scan belongs to another customer
502engine_submit_failedEngine unreachable; retry in 30s

Bulk lookup (up to 100 targets per call)

Business/Enterprise tiers. One POST returns an array of scan_id with a shared bulk_id. Each scan completes independently (its own webhook), every webhook includes the bulk_id field so you can group them. There's also an aggregate GET endpoint to poll the whole batch with one call instead of N.

POST/api/v1/scan/bulk
curl -X POST https://skopio.io/api/v1/scan/bulk \
  -H "Authorization: Bearer skopio_live_..." \
  -H "Content-Type: application/json" \
  -d '{"targets":[
    {"target":"alice@example.com","kind":"email"},
    {"target":"bob@example.com","kind":"email"},
    {"target":"+15551234567","kind":"phone"}
  ]}'

# Response 202
{
  "ok": true,
  "bulk_id": "bulk_xxx",
  "total": 3,
  "queued": 3,
  "rejected": [],
  "scans": [
    {"scan_id":"req-aaa","target":"alice@example.com","kind":"email","poll_url":"..."},
    {"scan_id":"req-bbb","target":"bob@example.com","kind":"email","poll_url":"..."},
    {"scan_id":"req-ccc","target":"+15551234567","kind":"phone","poll_url":"..."}
  ],
  "aggregate_poll_url": "https://skopio.io/api/v1/scan/bulk/bulk_xxx"
}
GET/api/v1/scan/bulk/{bulk_id}
# Aggregate poll — one call returns all scans in the batch
curl https://skopio.io/api/v1/scan/bulk/bulk_xxx \
  -H "Authorization: Bearer skopio_live_..."

# Response 200
{
  "ok": true,
  "bulk_id": "bulk_xxx",
  "total": 3,
  "queued": 1,
  "running": 0,
  "completed": 2,
  "failed": 0,
  "all_done": false,
  "scans": [
    {"scan_id":"req-aaa","status":"completed","target":"alice@example.com","result":{...}},
    {"scan_id":"req-bbb","status":"completed","target":"bob@example.com","result":{...}},
    {"scan_id":"req-ccc","status":"queued","target":"+15551234567"}
  ]
}
  • Max 100 targets per bulk call.
  • Rate limits: bulk_size + current_inflight must fit your tier (e.g. Business = 10 inflight). Also counts against rate/hour and rate/day.
  • Invalid targets land in rejected[]; valid ones still get queued.
  • Each scan completes independently. If a webhook is configured, every per-scan webhook carries the shared bulk_id field so you can group them.

Webhooks (push, no polling)

Set the URL in /app/api-keys/[id]. Skopio POSTs the result as soon as the scan completes. Each request is signed with HMAC-SHA256 — verify with your stored secret.

Headers sent on every webhook POST:

X-Skopio-Event:     scan.completed | scan.failed
X-Skopio-Signature: sha256=<hex>
X-Skopio-Delivery:  <delivery_id>
X-Skopio-Timestamp: <unix_ms>
User-Agent:         Skopio-Webhook/1
Content-Type:       application/json

Verify the signature (Node.js):

import {createHmac, timingSafeEqual} from "node:crypto";

function verify(rawBody, sigHeader, secret) {
  const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
  return timingSafeEqual(Buffer.from(expected), Buffer.from(sigHeader));
}

Body shape is the same as the GET response \`result\` field:

{
  "event": "scan.completed",
  "scan_id": "req-...",
  "bulk_id": "bulk_xxx",          // only present if scan was part of a bulk submission
  "target": "john@example.com",
  "kind": "email",
  "status": "completed",
  "completed_at": "2026-05-17T12:34:56Z",
  "result": {
    "risk": {"score": 65, "band": "medium"},
    "profiles": {...}, "breaches": [...], "ai_summary": "...",
    ...
  }
}

Retries: failed deliveries retry up to 3 times (immediate, +30s, +5min). Return any 2xx to acknowledge. All attempts are logged in /app/api-keys/[id].

Pricing & tiers

Tiers control THROUGHPUT only. Every API call deducts 1 credit from your account balance, regardless of tier. Exceeding rate-limit returns 429 with no deduction. Balance < 1 → 402 insufficient_balance.

Get API access →