Report API
The public v2 Report API returns urlquery analysis reports, their screenshots, and captured resources. It also lists recent and related reports and searches historical reports.
All routes are on https://api.urlquery.net and require x-apikey. Every result is filtered by what your API key may see, as described in report visibility.
| Method | Path | Quota class | Result |
|---|---|---|---|
GET | /public/v2/reports/{report_id}/overview | report_lookup | Compact report overview. |
GET | /public/v2/reports/{report_id} | report_lookup | Full report document. |
GET | /public/v2/reports/{report_id}/screenshot | report_lookup | Screenshot image (PNG). |
GET | /public/v2/reports/{report_id}/resources/{hash}/content | report_lookup | Captured resource from the report, as a download. |
DELETE | /public/v2/reports/{report_id} | report_lookup | Delete a report you own. |
GET | /public/v2/reports/recent | report_collection | Recent reports, newest first. |
GET | /public/v2/reports/related | report_collection | Recent public reports for the same target IP address, ASN, or domain. |
GET | /public/v2/reports/search | report_search | Search historical reports. |
Every call also consumes the account's overall v2 quota.
Interpretation
- Reports describe what the analysis environment observed when it visited a URL. They are evidence for an investigation, not a verdict on their own. See reports and evidence.
- Lists and search results contain only reports your key may see. A report you cannot see returns
404 NOT_FOUND, the same as a report that does not exist. - Captured content comes from potentially malicious sites. Store and inspect screenshots and resources only in an isolated environment.
Get a report
GET /public/v2/reports/{report_id}/overview
GET /public/v2/reports/{report_id}
report_id is a UUID. Start with the overview, and request the full report only when you need its evidence.
curl --fail-with-body \
--header "x-apikey: ${URLQUERY_API_KEY}" \
"https://api.urlquery.net/public/v2/reports/<REPORT_ID>/overview"
{
"success": true,
"data": {
"report_id": "<REPORT_ID>",
"status": "done",
"date": "2026-10-01T12:00:00Z",
"tags": ["phishing"],
"url": {"schema": "https", "addr": "example.com/login", "fqdn": "example.com", "domain": "example.com", "tld": "com"},
"ip": {"addr": "192.0.2.10", "port": 443, "asn": 64500, "as": "Example AS", "country": "Norway", "country_code": "NO"},
"final": {"url": {"schema": "https", "addr": "example.com/login", "fqdn": "example.com", "domain": "example.com", "tld": "com"}, "title": "Sign in"},
"access": "public",
"alert_count": {"ids": 0, "urlquery": 2, "analyzer": 1},
"hosts": [
{
"fqdn": "example.com",
"ip": {"addr": "192.0.2.10", "port": 443, "asn": 64500, "as": "Example AS", "country": "Norway", "country_code": "NO"},
"domain_registered": "2026-09-28",
"domain_rank": 0,
"first_seen": "2026-09-29T08:00:00Z",
"last_seen": "2026-10-01T12:00:00Z",
"request_count": 14,
"alert_count": 3,
"sent_data": 2048,
"received_data": 512000,
"technologies": ["nginx", "PHP"]
}
]
}
}
hosts lists every host the analysis contacted, with its IP and network, popularity rank (0 when unranked), registration date and passive DNS bounds when known, traffic and alert counts, and fingerprinted technologies. Lists and search results carry the same hosts, so you can group a set of reports by infrastructure without fetching each full report.
The full report is the stored report document. It has the overview's identity, URL, IP, final page, and tag fields at the top level, and adds HTTP transactions (http), JavaScript, detections, extracted artifacts, and submission details (submit, settings). Three overview fields sit elsewhere: visibility is settings.access, alert counts are stats.alert_count, and the hosts are summary, with technology details under fingerprints. New fields can be added over time; ignore fields you do not recognize.
Get report evidence
GET /public/v2/reports/{report_id}/screenshot
GET /public/v2/reports/{report_id}/resources/{hash}/content
The screenshot is returned as image/png. Resource content is returned as application/octet-stream with Content-Disposition: attachment, whatever its original type, so browsers do not render it. Both are sent with Cache-Control: private, no-store and X-Content-Type-Options: nosniff.
hash is the MD5, SHA-1, SHA-256, or SHA-512 of the resource, in hexadecimal. The resource must have been captured in that report, and you must be able to see the report. Find hashes in the full report, for example in the HTTP transactions.
curl --fail-with-body \
--header "x-apikey: ${URLQUERY_API_KEY}" \
--output resource.bin \
"https://api.urlquery.net/public/v2/reports/<REPORT_ID>/resources/<SHA256>/content"
Errors from these routes still use the JSON error envelope.
Delete a report
DELETE /public/v2/reports/{report_id}
Only the account that submitted a report can delete it. The response is 200 OK with an empty data object. For any other report, the response is 404 NOT_FOUND and nothing changes.
curl --fail-with-body --request DELETE \
--header "x-apikey: ${URLQUERY_API_KEY}" \
"https://api.urlquery.net/public/v2/reports/<REPORT_ID>"
List recent reports
GET /public/v2/reports/recent
| Parameter | Values |
|---|---|
scope | all (default): your own reports and reports open to your plan. user: your own reports. team: your team's reports; empty if you are not in a team. alerted: reports from the last year with detections from your or your team's detection rules. |
limit | 1 through 100; defaults to 25. |
cursor | Opaque continuation token from the prior page. |
curl --fail-with-body --get \
--header "x-apikey: ${URLQUERY_API_KEY}" \
--data-urlencode 'scope=user' \
--data-urlencode 'limit=25' \
https://api.urlquery.net/public/v2/reports/recent
List related reports
GET /public/v2/reports/related
| Parameter | Values |
|---|---|
scope | ip, asn, or domain. Required. |
value | An IPv4 or IPv6 address, an ASN from 0 through 4294967295, or a domain of at most 253 characters. Required. |
limit | 1 through 100; defaults to 25. |
cursor | Opaque continuation token from the prior page. |
Related reports match the report's target, meaning the IP address, ASN, or domain of the analyzed URL, not every server the page contacted. They include public reports only, plus restricted reports for accounts with restricted access when scope=ip.
curl --fail-with-body --get \
--header "x-apikey: ${URLQUERY_API_KEY}" \
--data-urlencode 'scope=domain' \
--data-urlencode 'value=example.com' \
https://api.urlquery.net/public/v2/reports/related
Search reports
GET /public/v2/reports/search
| Parameter | Values |
|---|---|
query | Search expression, up to 8192 characters. Required. See the search syntax guide. |
limit | 1 through 100; defaults to 25. |
offset | 0 through 10000; defaults to 0. offset plus limit must be at most 10000. |
Search covers reports from the last year. Narrow it with a date range in the query, for example date:[2026-05-01 TO 2026-05-09].
curl --fail-with-body --get \
--header "x-apikey: ${URLQUERY_API_KEY}" \
--data-urlencode 'query=tags:phishing AND date:[2026-05-01 TO 2026-05-09]' \
--data-urlencode 'limit=25' \
https://api.urlquery.net/public/v2/reports/search
{
"success": true,
"data": {
"query": "tags:phishing AND date:[2026-05-01 TO 2026-05-09]",
"items": [],
"page": {"limit": 25, "offset": 0, "total_hits": 42}
}
}
items uses the overview format. Results are filtered by visibility after matching, so a page can hold fewer than limit items, and total_hits counts matches before that filtering. To read the next page, add limit to offset; do not use the number of items returned.
Pagination
Recent and related lists place items and page under data:
{
"success": true,
"data": {
"items": [],
"page": {
"limit": 25,
"next_cursor": "<OPAQUE_CURSOR>",
"has_more": true
}
}
}
Send next_cursor unchanged as cursor with the same scope and value until has_more is false. Do not parse or modify it. A page can hold fewer than limit items, or none, while has_more is true, because visibility is checked after a page is selected. An empty collection returns 200 OK with items: [].
V2 errors
{
"success": false,
"error": {
"http_code": 404,
"error": "NOT_FOUND",
"message": "the requested resource was not found"
}
}
| Status | Error | When |
|---|---|---|
400 | BAD_REQUEST | A parameter is missing or invalid, a cursor was reused with different parameters, or the search query is invalid. |
401 | MISSING_API_KEY | The x-apikey header is missing. |
403 | INVALID_API_KEY | The key is invalid or disabled, or could not be verified. |
403 | FORBIDDEN | Your key is not allowed to read reports. |
404 | NOT_FOUND | The report or resource does not exist, or you cannot see it. |
429 | RATE_LIMIT_EXCEEDED | A quota is exhausted. Wait for Retry-After seconds. |
502 | BAD_GATEWAY | The report service failed or is unavailable. Retry later. |
503 | RATE_LIMIT_UNAVAILABLE | Quota enforcement is temporarily unavailable. Retry later. |
504 | GATEWAY_TIMEOUT | The report service timed out. Retry later. |
500 | INTERNAL_ERROR | An unexpected failure. Retry later. |