Skip to main content

Submission API

The v2 Submission API queues a URL for analysis in urlquery's isolated browser and reports the analysis progress. When the analysis is done, read the result with the Report API.

All routes are on https://api.urlquery.net and require x-apikey.

MethodPathQuota classResult
POST/public/v2/submissionssubmission_createQueue a URL. Returns the submission, including the future report_id.
GET/public/v2/submissions/{queue_id}submission_lookupProgress of a submission made by you or your team.

Every call also consumes the account's overall v2 quota, so the overall quota can limit you before the submission quota does.

Before you submit​

  • Search first. An existing report may already answer the question without visiting the URL again. See report search.
  • Submit only URLs you are authorized to analyze, and remove tokens, personal data, and internal names first. See safe use.
  • Choose visibility on purpose. public is the default. See choosing visibility.

Submit a URL​

POST /public/v2/submissions
curl --fail-with-body --request POST \
--header "x-apikey: ${URLQUERY_API_KEY}" \
--header 'content-type: application/json' \
--data '{"url":"https://login.example.com/verify","access":"restricted","run_time":60,"tags":["case_1234"]}' \
https://api.urlquery.net/public/v2/submissions

The body is one JSON object of at most 64 KiB. Unknown fields are rejected.

FieldRequiredValuesDefaultDescription
urlyesAbsolute http or https URL, up to 8192 characters—The page to open. A URL without a scheme is rejected rather than guessed.
accessnopublic, restricted, privatepublicWho can see the report. It also sets how long the report is kept; see data retention.
run_timeno20 to 20026Seconds the browser stays on the page. Increase it for pages that wait before redirecting or loading content.
useragentnoUp to 512 charactersA current desktop FirefoxThe browser identity the page sees.
device_typenodesktop, mobile, tabletdesktopThe device type reported to the page.
tagsnoUp to 20, each 1–64 letters, digits, or _noneYour own labels, stored as submit.tags. Search for them later, for example submit.tags:case_1234.
metanoUp to 20 string pairs; keys up to 64 characters, values up to 1024noneYour own context, such as a case ID. Visible only to you and your team. Never put secrets here.
sanitize_urlnotrue, falsefalseReplaces a base64-encoded email address at the end of the URL path with a placeholder address. It does not remove other personal data.

The response is the submission with status set to queued:

{
"success": true,
"data": {
"queue_id": "0d3f8a8e-2b7c-4f41-9a37-5b8f0c6d7e21",
"report_id": "6f1c1a52-8d4e-4f0e-b1a1-1c2d3e4f5a6b",
"status": "queued",
"url": {"schema": "https", "addr": "login.example.com/verify", "fqdn": "login.example.com", "domain": "example.com", "tld": "com"},
"ip": {"addr": "203.0.113.10", "port": 443, "asn": 64500, "as": "EXAMPLE-AS", "country": "Norway", "country_code": "NO"},
"access": "restricted",
"run_time": 60,
"device_type": "desktop",
"useragent": "Mozilla/5.0 …",
"sanitize_url": false,
"tags": ["case_1234"],
"meta": {}
}
}

Save both IDs. queue_id follows the analysis; report_id is the report it will produce.

Which URLs are accepted​

The gateway checks the target before queuing it:

  • The host must resolve, and every address it resolves to must be public. Private, loopback, link-local, carrier-grade NAT, multicast, reserved, and documentation ranges are rejected, as are IP-literal URLs in those ranges.
  • .onion hosts are accepted without resolution.
  • Some domains are not accepted for analysis. They return 400 with the message the URL cannot be submitted.
MessageCause
url must be an absolute http or https URLMissing scheme, another scheme, or no host.
domain does not resolveThe host has no DNS answer. Check the spelling, or retry later if it is newly registered.
url resolves to a non-public addressAt least one address is not on the public internet.
the URL cannot be submittedThe analysis service refused the URL, for example a blocked domain.

Follow a submission​

GET /public/v2/submissions/{queue_id}
curl --fail-with-body \
--header "x-apikey: ${URLQUERY_API_KEY}" \
"https://api.urlquery.net/public/v2/submissions/${QUEUE_ID}"

Only the account that submitted, or a member of its team, can read a submission. Any other queue_id returns 404 NOT_FOUND.

statusMeaning
queuedWaiting for an analysis browser.
processingThe browser is visiting the URL.
analyzingThe visit is over and detections are running.
doneThe report is ready. Read it by report_id.
failedThe analysis did not produce a report. Submitting again later can succeed.

A normal analysis takes about 30 seconds plus run_time, depending on load. Poll every 5 to 10 seconds; each poll counts against submission_lookup.

import time

submission = call("POST", "/submissions", json={"url": "https://login.example.com/verify", "access": "restricted"})
while submission["status"] not in ("done", "failed"):
time.sleep(10)
submission = call("GET", f"/submissions/{submission['queue_id']}")

if submission["status"] == "done":
overview = call("GET", f"/reports/{submission['report_id']}/overview")

call is the helper from the API workflows.

The submission object​

FieldDescription
queue_idThe submission ID.
report_idThe ID the finished report will have. The report can be read once status is done.
statusqueued, processing, analyzing, done, or failed.
urlThe URL as it will be analyzed, split into schema, addr, fqdn, domain, and tld.
ipThe address the target resolved to when queued, with ASN and country. 0.0.0.0 for .onion hosts.
access, run_time, device_type, useragent, sanitize_urlThe settings the analysis uses, with defaults filled in.
tags, metaYour labels and context, as stored.

Retries and duplicates​

POST /submissions is not idempotent: each successful call queues a new analysis and consumes quota.

  • Do not retry a submit automatically after a timeout or a 502/504. The analysis may already be queued. Tag your submissions, wait for the analysis time to pass, and search for the tag (submit.tags:case_1234) or list your recent reports (scope=user) before submitting again.
  • Retry after 429 once Retry-After has passed, and after 503; those requests were not admitted.
  • Polling with GET /submissions/{queue_id} is safe to retry.

Errors​

StatusErrorCause
400BAD_REQUESTInvalid body or field, or a URL that cannot be analyzed. message says which.
401MISSING_API_KEYNo x-apikey header.
403INVALID_API_KEYThe key is invalid or disabled.
403FORBIDDENThe analysis service refused the key.
404NOT_FOUNDThe submission does not exist or belongs to another account.
429RATE_LIMIT_EXCEEDEDThe submission or overall quota is used up. Wait Retry-After seconds.
502, 504BAD_GATEWAY, GATEWAY_TIMEOUTThe analysis service failed or timed out. For a submit, check whether it was queued before retrying.
503RATE_LIMIT_UNAVAILABLEQuota enforcement is temporarily unavailable. Retry later.