Skip to content

API reference/ip

Download a bulk IP job's results

GET/api/v1/ip/bulk/{jobId}/results

authentication
Secret API key as a bearer token
billing
Free.

The result file as CSV: one row per input address, in input order, with the fields of the single-address response as columns plus the credits it cost. Two structured fields are flattened: sanctions as its codes joined with ;, account_list as kind:name. Left out, because they belong to one live request rather than a row: timezone_matches (needs a browser), seen_by_you (would count the run's own rows), and the opt-in network fields (abuse_contact, network_details, the hostname fields). Fields containing spaces are quoted. Add ?variant=low|medium|high to download only the rows of one risk level; the default full returns everything. Available once the job is completed, or cancelled or failed with at least one processed row — those rows were billed, so they stay downloadable. A job still running answers 409 RESULTS_NOT_READY, a cancelled job with nothing processed 409 JOB_CANCELLED, and a failed job with nothing processed 409 JOB_FAILED.

format=json answers the same rows, filtered the same way, as objects keyed by the result file's own header instead of a CSV attachment: { "success": true, "data": { "job": …, "results": [ … ] }, "meta": { "variant", "request_id" } }. It is streamed row by row exactly as the CSV is, so a 100,000-row run costs no more to answer either way. A CSV cell carries no type, so only the two the writer encodes come back typed: an empty cell is null and true/false are booleans. Everything else is the string as written — a number is never silently made numeric, because postcodes, phone numbers and house numbers lose their leading zeros that way — and the columns of an uploaded file come back exactly as they were uploaded.

Parameters

name in type description
jobIdrequiredpathinteger
variantquerystringRestrict the file to one risk level. Defaults to full. One of: full, low, medium, high.
formatquerystring`csv` streams the file as an attachment; `json` streams the same rows as objects. One of: csv, json. Default: csv.

Example request

curl https://spaw.co/api/v1/ip/bulk/{jobId}/results \
  -H "Authorization: Bearer sk_live_…"
const response = await fetch('https://spaw.co/api/v1/ip/bulk/{jobId}/results', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer sk_live_…',
  },
});
const result = await response.json();
import requests

response = requests.get(
    'https://spaw.co/api/v1/ip/bulk/{jobId}/results',
    headers={'Authorization': 'Bearer sk_live_…'},
)
result = response.json()
$ch = curl_init('https://spaw.co/api/v1/ip/bulk/{jobId}/results');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$result = json_decode(curl_exec($ch), true);
from spaw import Client

client = Client('sk_live_…')
result = client.download_ip_bulk_results(job_id)
import Spaw from 'spaw';

const spaw = new Spaw({ apiKey: 'sk_live_…' });
const result = await spaw.downloadIpBulkResults(jobId);
use Spaw\Client;

$spaw = new Client('sk_live_…');
$result = $spaw->downloadIpBulkResults($jobId);

This endpoint has no console on its page. It answers with records from your own account, often by an id only you have, rather than about a value you can type here. The dashboard lists the same rows.

Responses

200The result rows, as a CSV attachment or as streamed JSON.

ip,version,reason,network,continent,country,country_name,is_eu,is_eea,privacy_regime,sanctions,fatf_list,region,city,latitude,longitude,location_source,location_confidence,timezone,timezone_source,currency,calling_code,asn,org,asn_type,connection_type,is_mobile,is_satellite,registry,registry_country,allocated_at,is_datacenter,hosting_provider,is_corporate_proxy,corporate_proxy,is_crawler,crawler,crawler_kind,is_blocklisted,blocklist,is_attack_source,attack_source,is_tor,tor_last_seen,is_vpn,is_relay,privacy_service,is_anonymous,account_list,risk_score,risk_level,credits_used
8.8.8.8,4,,8.8.8.0/24,NA,US,United States,false,false,ccpa,,,California,Mountain View,37.4056,-122.0775,database,low,America/Los_Angeles,region,USD,1,15169,Google LLC,hosting,hosting,false,false,arin,US,2023-12-28,true,,false,,false,,,false,,false,,false,,false,false,,false,,40,medium,1

401The key is missing, malformed, or revoked.

{
    "success": false,
    "error": {
        "code": "UNAUTHENTICATED",
        "message": "Provide a valid API key as a bearer token.",
        "request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
    }
}

404No such record on this account.

{
    "success": false,
    "error": {
        "code": "NOT_FOUND",
        "message": "No record with that id on this account.",
        "request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
    }
}

409The job is still running (`RESULTS_NOT_READY`), or was cancelled or failed before any row was processed (`JOB_CANCELLED`, `JOB_FAILED`).

{
    "success": false,
    "error": {
        "code": "RESULTS_NOT_READY",
        "message": "The results are not ready yet.",
        "request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
    }
}

429Over 5 requests per second for the key. Retry after the limit resets.

{
    "success": false,
    "error": {
        "code": "RATE_LIMITED",
        "message": "Too many requests. Retry after the limit resets.",
        "request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
    }
}

Error codes

Failures answer { success: false, error: { code, message, request_id } }. Each code has its own page.

markdown version·openapi.json