API Reference¶
This page is the technical reference for the IP Intelligence API: base URL, authentication, request and response schemas, every stable error code, and pagination and rate-limit mechanics.
It assumes you have already read Using the API and have a key. The query syntax — supported search fields, combination rules, and what each one searches — is defined once, in Supported searches; this page does not repeat it, only how to send and read it over HTTP.
Base URL¶
https://api.ip.alphastrike.io
Every route below is under /v1. There is currently one version; a route or field is not
removed or repurposed within it.
Authentication¶
Send your key as a bearer credential on every request:
Authorization: Bearer ipk_live_7pQ2m9XvB4hK1sR6tY0wZ3nC8dF5gJaL
A request with no Authorization header, a malformed one, or a key that is unknown or has been
revoked all answer the same 401 unauthorized — the response does not distinguish which, so it
cannot be used to test whether a particular key string is valid. See
Creating a key and
Allowance, revocation, and security for
how keys are created, revoked, and should be handled.
Request conventions¶
- Send
Content-Type: application/jsonwith every request body.GETroutes take no body. - A body must be valid JSON, at most 512 KB, and contain only the fields documented for that request. An unrecognized field is rejected rather than silently ignored — build your client to send only the fields shown below, so a future optional response field does not require a client change.
- Every response — success or error — carries a
request_idstring in the body and the same value in theX-Request-IDresponse header. Include it when reporting a problem. - Timestamps are RFC 3339, always UTC (a trailing
Z, never a UTC offset).
Rate limits and the daily allowance¶
Only POST /v1/search draws on the daily allowance described in
How much you can search. GET /v1/allowance,
GET /v1/search/capabilities, POST /v1/search/count, POST /v1/search/facets,
POST /v1/search/exposure, and POST /v1/hosts/detail never consume it, regardless of outcome —
call them as often as you need.
Within POST /v1/search, a request is charged only when it answers with 200 — including a
successful response with zero hosts, because that is an answer too. Every other outcome —
a validation error, an auth or plan problem, a rate or capacity limit, a timeout, or an expired
cursor — costs nothing and can be retried immediately without spending your allowance.
Read the allowance object on every 200 response instead of polling GET /v1/allowance
between searches; it already reflects the search you just ran. Poll GET /v1/allowance only
when you have not searched recently and want to check before you do — asking never spends one.
A separate, independent limit caps how many searches your account can have running at the same
time; a request beyond it answers 429 user_concurrency_reached and, like every non-200
outcome, is free to retry once an earlier request finishes.
Pagination¶
POST /v1/search returns next_cursor when more results exist: an opaque sc1_ handle, 36
characters in total. Send it back as cursor in the next request with the same query and
limit to get the following page. It is:
- bound to that exact query and limit — changing either while paging answers
422 validation_failed; - one-time — it cannot be replayed once used;
- short-lived — it expires after a bounded window.
A missing, expired, replayed, or otherwise invalid cursor answers 410 cursor_gone. Start over
from an unset cursor; the fresh first page is a normal, chargeable search, but the expired attempt
itself was free. A page holds at most 100 hosts, ordered ascending by IP address, so paging
through the same query in the same order always produces the same sequence of hosts.
POST /v1/hosts/detail paginates a single host's own services with its own, separate
continuation string (up to 512 characters, no fixed format), scoped to that one IP and returned
only from that endpoint. It is unrelated to the sc1_ search cursor and the two are not
interchangeable.
Endpoints¶
GET /v1/search/capabilities¶
The current supported-protocol registry — the same list Add filter loads. Call it rather than
hardcoding protocol names, and use catalog_version to detect when the list has changed instead
of refetching on a timer; the response is not intended for an HTTP cache (Cache-Control:
no-store).
curl -s -H "Authorization: Bearer ipk_live_7pQ2m9XvB4hK1sR6tY0wZ3nC8dF5gJaL" \
https://api.ip.alphastrike.io/v1/search/capabilities
| Field | Type | Notes |
|---|---|---|
request_id |
string | |
query_registry_version |
string | Currently the fixed value service-v5. |
catalog_version |
string | Opaque; changes when the supported set changes. |
protocols[] |
array | Exactly 87 entries today, sorted lexicographically. |
protocols[].value |
string | Canonical lowercase token, for example https. |
protocols[].ports[] |
integer[] | Sorted, unique ports this token's catalog covers. |
protocols[].ipv4 / .ipv6 |
boolean | Whether the token applies to that address family. |
protocols[].evidence_kind |
string | summary, http, or ssh — which typed evidence shape Selected host shows for it. |
{
"request_id": "4f2b6b7a2b6a4c6a9e6e2b7a2b6a4c6a",
"query_registry_version": "service-v5",
"catalog_version": "2026-08-01-v7",
"protocols": [
{ "value": "http", "ports": [80, 8000, 8080], "ipv4": true, "ipv6": true, "evidence_kind": "http" },
{ "value": "https", "ports": [443, 8443], "ipv4": true, "ipv6": true, "evidence_kind": "http" },
{ "value": "ssh", "ports": [22], "ipv4": true, "ipv6": true, "evidence_kind": "ssh" }
]
}
(showing 3 of 87 — the response is not truncated; fetch it to see the current full list.)
GET /v1/allowance¶
Your remaining daily searches, without spending one. Covered fully in Allowance, revocation, and security:
curl -s -H "Authorization: Bearer ipk_live_7pQ2m9XvB4hK1sR6tY0wZ3nC8dF5gJaL" \
https://api.ip.alphastrike.io/v1/allowance
{ "request_id": "4f2b6b7a2b6a4c6a9e6e2b7a2b6a4c6a", "allowance": { "limit": 20, "used": 6, "remaining": 14, "resets_at": "2026-08-12T00:00:00Z" } }
If a response has no allowance field at all — here or on POST /v1/search — no daily limit
applies to that account. That is not the same as none remaining.
POST /v1/search¶
The matching hosts for a query, exactly like the search box. limit defaults to 25 and accepts up
to 100. See Supported searches for the query grammar.
curl -s -H "Authorization: Bearer ipk_live_7pQ2m9XvB4hK1sR6tY0wZ3nC8dF5gJaL" \
-H "Content-Type: application/json" \
-d '{"query":"organization:\"Alpha Strike\" protocol:https","limit":2}' \
https://api.ip.alphastrike.io/v1/search
| Field | Type | Notes |
|---|---|---|
query |
string | 1–512 characters. |
limit |
integer | 1–100, default 25. |
cursor |
string | From a previous response's next_cursor; see Pagination. |
The response is one entry per unique IP. services.items is the bounded five-row preview
described in Matching services; a service carries
match_reasons only when your query named a service, port, product, certificate, CPE, CVE, CVSS,
or exploit clause. organization_matches, domain_matches, and asn_matches appear only for
that respective search type.
{
"request_id": "4f2b6b7a2b6a4c6a9e6e2b7a2b6a4c6a",
"results": [
{
"ip": "203.0.113.10",
"location": { "country_code": "DE", "country_name": "Germany", "city": "Frankfurt am Main", "latitude": 50.1109, "longitude": 8.6821, "accuracy_radius_km": 50 },
"network": { "asn": 64496, "organization": "Alpha Strike Labs GmbH" },
"whois": { "organization": "Alpha Strike Labs GmbH", "netname": "ALPHASTRIKE-NET", "cidr": "203.0.113.0/24", "registry": "ripe", "country_code": "DE" },
"reverse_dns": "host-10.example.com",
"domains": { "items": [
{ "name": "host-10.example.com", "source": "reverse_dns" },
{ "name": "www.example.com", "source": "certificate_subject" }
], "has_more": false },
"services": {
"items": [
{
"port": 443,
"protocol": "https",
"observed_at": "2026-08-10T03:14:00Z",
"product": "F5 NGINX",
"version": "1.26.1",
"certificate_names": ["example.com"],
"vulnerabilities": { "cve_count": 0, "has_exploits": false },
"evidence": [
{ "kind": "http", "status_code": 200, "title": "Example", "server": "nginx",
"tls": { "version": "TLSv1.3", "cipher_suite": "TLS_AES_128_GCM_SHA256", "issuer_common_name": "Example Fixture CA", "valid_from": "2026-06-01T00:00:00Z", "valid_until": "2026-08-30T00:00:00Z", "fingerprint_sha256": "b1946ac92492d2347c6235b4d2611184b1946ac92492d2347c6235b4d2611184", "browser_trusted": true } }
],
"match_reasons": ["protocol:https"]
}
],
"has_more": false
},
"organization_matches": [
{ "source": "as_organization", "value": "Alpha Strike Labs GmbH" }
]
}
],
"next_cursor": "sc1_ab12CD34ef56GH78ij90KL12mn34OP56",
"allowance": { "limit": 20, "used": 7, "remaining": 13, "resets_at": "2026-08-12T00:00:00Z" }
}
A domain query adds domain_resolution at the top level instead of organization_matches, and
each result carries domain_matches instead:
{
"domain_resolution": { "status": "resolved", "address_count": 2 },
"results": [
{ "ip": "203.0.113.10", "services": { "items": [], "has_more": false },
"domains": { "items": [], "has_more": false },
"domain_matches": [ { "source": "dns_resolution", "value": "example.com" } ] }
]
}
POST /v1/search/count¶
An approximate unique-host estimate for an ASN or a filter query, independent of any loaded page — what the result count above a filter search shows. Not accepted for exact-IP, CIDR, or domain queries.
curl -s -H "Authorization: Bearer ipk_live_7pQ2m9XvB4hK1sR6tY0wZ3nC8dF5gJaL" \
-H "Content-Type: application/json" \
-d '{"query":"asn:64496"}' \
https://api.ip.alphastrike.io/v1/search/count
{ "request_id": "4f2b6b7a2b6a4c6a9e6e2b7a2b6a4c6a", "unique_hosts": { "value": 128, "relation": "approximate" } }
relation is exact only where the underlying count genuinely is one; treat approximate as an
estimate, not an off-by-a-little count.
POST /v1/search/facets¶
The Suggested Filters behind a service, organization, or country search — six fixed groups,
described in Suggested Filters. Returns
facets_not_supported for exact-IP, CIDR, domain, or ASN queries, matching the guide.
curl -s -H "Authorization: Bearer ipk_live_7pQ2m9XvB4hK1sR6tY0wZ3nC8dF5gJaL" \
-H "Content-Type: application/json" \
-d '{"query":"protocol:https"}' \
https://api.ip.alphastrike.io/v1/search/facets
{
"request_id": "4f2b6b7a2b6a4c6a9e6e2b7a2b6a4c6a",
"partial": true,
"can_add_filters": true,
"groups": [
{ "field": "port", "status": "available", "items": [ { "value": "443", "clause": "port:443", "selected": false, "unique_hosts": { "value": 842, "relation": "approximate" } } ] },
{ "field": "protocol", "status": "available", "items": [ { "value": "https", "clause": "protocol:https", "selected": true, "unique_hosts": { "value": 1240, "relation": "approximate" } } ] },
{ "field": "product", "status": "available", "items": [ { "value": "F5 NGINX", "clause": "product:\"F5 NGINX\"", "selected": false, "unique_hosts": { "value": 210, "relation": "approximate" } } ] },
{ "field": "country", "status": "available", "items": [ { "value": "DE", "clause": "country:DE", "selected": false, "unique_hosts": { "value": 96, "relation": "approximate" } } ] },
{ "field": "cvss", "status": "unavailable", "items": [], "error_code": "facet_unavailable" },
{ "field": "exploit", "status": "timed_out", "items": [], "error_code": "facet_timeout" }
]
}
Each of the 6 groups is independent: partial: true here means the cvss and exploit groups
did not come back, not that the whole response failed. Use the available groups and, if
you want the rest, retry the request — a retry costs nothing, since this route is never metered.
can_add_filters turns false once three filter clauses are already active, matching the limit
in Supported searches.
POST /v1/search/exposure¶
The bounded geographic aggregation behind the Globe view, described in Visualize geographic exposure. Not available for domain queries yet.
curl -s -H "Authorization: Bearer ipk_live_7pQ2m9XvB4hK1sR6tY0wZ3nC8dF5gJaL" \
-H "Content-Type: application/json" \
-d '{"query":"organization:\"Alpha Strike\"","resolution":"regional"}' \
https://api.ip.alphastrike.io/v1/search/exposure
| Field | Type | Notes |
|---|---|---|
query |
string | Any supported non-domain search. |
resolution |
string | global (default), regional, or local — coarsest to finest. |
{
"request_id": "4f2b6b7a2b6a4c6a9e6e2b7a2b6a4c6a",
"unique_hosts": { "value": 842, "relation": "approximate" },
"service_count": 1310,
"resolution": "regional",
"geotile_precision": 6,
"locations_relation": "complete",
"locations": [
{ "key": "6/33/22", "latitude": 50.11, "longitude": 8.68, "unique_hosts": 41, "service_count": 63, "country_code": "DE", "country_count": 1 },
{ "key": "6/34/22", "latitude": 48.85, "longitude": 2.35, "unique_hosts": 12, "service_count": 18 }
]
}
key is an opaque bucket address (an Elasticsearch geotile coordinate), not a place name — label
a cluster from country_code, never from key or from the coordinates. The second bucket above
omits country_code and country_count on purpose: that is the documented case where no host in
the bucket carries a GeoIP country, and a client must fall back to the coordinates rather than
inventing a country. locations_relation: "bounded" means the 500-bucket response limit was
reached; unique_hosts and service_count still describe the complete matching result set either
way.
POST /v1/hosts/detail¶
The complete current service inventory for one exact IP — the same data behind Host investigation and Selected host. Never metered, so open it as often as you like.
curl -s -H "Authorization: Bearer ipk_live_7pQ2m9XvB4hK1sR6tY0wZ3nC8dF5gJaL" \
-H "Content-Type: application/json" \
-d '{"ip":"203.0.113.10","limit":2}' \
https://api.ip.alphastrike.io/v1/hosts/detail
| Field | Type | Notes |
|---|---|---|
ip |
string | Exact IPv4 or IPv6 address. |
limit |
integer | 1–20 services per page. |
cursor |
string | From a previous response's next_cursor; see Pagination. |
{
"request_id": "4f2b6b7a2b6a4c6a9e6e2b7a2b6a4c6a",
"host": {
"ip": "203.0.113.10",
"location": { "country_code": "DE", "country_name": "Germany", "city": "Frankfurt am Main", "latitude": 50.1109, "longitude": 8.6821, "accuracy_radius_km": 50 },
"network": { "asn": 64496, "organization": "Alpha Strike Labs GmbH" },
"whois": { "organization": "Alpha Strike Labs GmbH", "netname": "ALPHASTRIKE-NET", "cidr": "203.0.113.0/24", "registry": "ripe", "country_code": "DE" },
"reverse_dns": "host-10.example.com",
"domains": { "items": [
{ "name": "host-10.example.com", "source": "reverse_dns" },
{ "name": "www.example.com", "source": "certificate_subject" }
], "has_more": false },
"services": {
"items": [
{ "port": 22, "protocol": "ssh", "observed_at": "2026-08-10T03:12:00Z",
"evidence": [ { "kind": "ssh", "protocol_version": "2.0", "software_version": "OpenSSH_9.6", "host_key_algorithm": "ssh-ed25519", "host_key_fingerprint": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" } ] },
{ "port": 443, "protocol": "https", "observed_at": "2026-08-10T03:14:00Z",
"product": "F5 NGINX", "version": "1.26.1", "certificate_names": ["example.com"],
"vulnerabilities": { "cve_count": 0, "has_exploits": false },
"evidence": [ { "kind": "http", "status_code": 200, "server": "nginx" } ] }
],
"has_more": true
}
},
"next_cursor": "eyJvIjoyLCJpcCI6IjIwMy4wLjExMy4xMCJ9"
}
host_not_found (404) means no current observation exists for that exact address — the same
outcome Host investigation shows as "No stored hosts."
Errors¶
Every error uses the same shape:
{ "request_id": "4f2b6b7a2b6a4c6a9e6e2b7a2b6a4c6a", "code": "validation_failed", "message": "request validation failed", "violations": [ { "field": "query", "code": "invalid_organization" } ] }
violations is only ever non-empty for validation_failed, and it is how you find out which
part of the request was rejected — code alone tells you the request was invalid, not why.
Top-level codes¶
code |
HTTP status | Meaning | Costs your allowance? |
|---|---|---|---|
invalid_json |
400 | The body is not valid JSON. | No |
organization_required |
400 | Your account does not currently resolve to exactly one organization. Contact us. | No |
unauthorized |
401 | The key is missing, malformed, unknown, or revoked. | No |
plan_expired |
402 | Your plan does not currently include search access. | No |
validation_failed |
422 | The query, limit, resolution, or cursor is invalid — see violations. |
No |
search_limit_reached |
429 | Your daily allowance for the current period is used up. | No |
user_concurrency_reached |
429 | You already have the maximum number of searches running at once. Wait for one to finish. | No |
cursor_gone |
410 | The search cursor is missing, expired, already used, or invalid after a restart. | No |
host_not_found |
404 | No current host exists for this exact IP (/v1/hosts/detail only). |
— (this route is never metered) |
search_capacity_exhausted |
503 | The service is at capacity and declined the search before starting it. | No |
commercial_access_unavailable |
503 | Your account's plan could not be confirmed. This is on our side. | No |
search_source_busy |
503 | The stored scan data was temporarily busy. Wait a few seconds and retry. | No |
service_unavailable |
503 | A dependency the request needs is unavailable. | No |
audit_unavailable |
503 | The mandatory audit record could not be written, so the request was not run. | No |
search_timeout |
504 | The search did not finish within its time budget. Narrow the query or retry. | No |
facet_timeout |
504 | The suggested-filter groups did not finish within their time budget (/v1/search/facets only). |
— (this route is never metered) |
As Rate limits and the daily allowance explains, the only
outcome that ever costs you a search is a 200 from POST /v1/search — every code in this table
is free.
Violation codes (validation_failed)¶
These appear in violations[].code with violations[].field set to "query" unless noted.
code |
Meaning |
|---|---|
invalid_query_syntax |
The query does not parse as a supported expression. |
unknown_field |
A field name is not one of the approved filters. |
duplicate_field |
The same field appears more than once in one query. |
unsupported_operator |
Something other than a space or uppercase AND was used to combine clauses. |
invalid_port |
The port is missing, non-numeric, or out of range. |
unsupported_protocol |
Not an exact value from GET /v1/search/capabilities. |
invalid_organization |
The organization phrase is missing its quotes or is otherwise malformed. |
invalid_product |
The product phrase is missing its quotes or is otherwise malformed. |
invalid_certificate_name |
Not one exact DNS name, or a wildcard was used. |
invalid_country |
Not an uppercase ISO-2 code. |
invalid_cpe |
Not one complete, quoted CPE 2.3 URI. |
invalid_cve |
Not the exact CVE-YYYY-NNNN form. |
invalid_cvss |
Not an inclusive min-max range within 0.0–10.0. |
invalid_exploit |
Not exactly yes or no. |
invalid_cidr |
The network does not parse as an IPv4 CIDR. |
cidr_too_broad |
The prefix is broader than /24. |
ipv6_cidr_not_supported |
IPv6 CIDR search is not available yet. |
invalid_domain |
Not a registrable domain name, or a wildcard or public-suffix-only value was used. |
invalid_asn |
Not the standalone decimal form asn:<1..4294967295>. |
query_too_broad |
The query names no service and matches more hosts than one search can return (POST /v1/search only — combine it with a port, protocol, or country). |
unsupported_query |
The query does not match any supported search type. |
count_not_supported |
This query type cannot be counted (POST /v1/search/count only — exact-IP, CIDR, and domain are not accepted). |
exposure_not_supported |
This query type cannot be aggregated yet (POST /v1/search/exposure only — domain queries are excluded). |
facets_not_supported |
This query type has no Suggested Filters (POST /v1/search/facets only — exact-IP, CIDR, domain, and ASN are excluded). |
unsupported_resolution |
(field: "resolution") Not global, regional, or local (POST /v1/search/exposure only). |
invalid_ip |
(field: "ip") Not a valid IPv4 or IPv6 address (POST /v1/hosts/detail only). |
invalid / query_mismatch |
(field: "cursor") The cursor is malformed, or does not match the query and limit it was issued for (POST /v1/search only — a cursor problem otherwise answers 410 cursor_gone, not this). |
The Troubleshooting searches table describes the same conditions in the wording the web app shows for each one.
Building a client against this API¶
- Handle every code in the tables above, not just the ones you expect during development —
service_unavailable,search_source_busy, and the two timeout codes are ordinary operating conditions for a bounded, best-effort search service, not edge cases. - Only
plan_expired,organization_required, andunauthorizedneed a human to act. Every other non-200response — timeouts, capacity, concurrency, the daily limit, and an expired cursor — is safe to retry: back off briefly for a503or504, and wait forresets_atbefore retryingsearch_limit_reached. - Page with
next_cursoruntil it is absent, and treat410 cursor_goneas "start again from an unset cursor" rather than an error to surface to a person. - Read
allowancefrom each response rather than tracking your own count; it is authoritative and already includes the request that returned it. - Include
request_idin anything you log or report — it is the fastest way to have a specific request investigated.
A minimal pagination loop, stopping when the allowance runs out or the query is exhausted:
import requests
API = "https://api.ip.alphastrike.io"
KEY = "ipk_live_7pQ2m9XvB4hK1sR6tY0wZ3nC8dF5gJaL"
def search_all(query, limit=100):
cursor = None
while True:
body = {"query": query, "limit": limit}
if cursor:
body["cursor"] = cursor
response = requests.post(
f"{API}/v1/search",
headers={"Authorization": f"Bearer {KEY}"},
json=body,
timeout=30,
)
if response.status_code != 200:
error = response.json()
raise RuntimeError(f"{error['code']}: {error['message']} (request_id={error['request_id']})")
payload = response.json()
yield from payload["results"]
allowance = payload.get("allowance")
if allowance and allowance["remaining"] <= 0:
break
cursor = payload.get("next_cursor")
if not cursor:
break
for host in search_all('organization:"Alpha Strike" protocol:https'):
print(host["ip"])
This omits retry/backoff for brevity; add it around the requests.post call for 503 and 504
per the guidance above before relying on this in production.