/v1 contract, newest first. Additive changes (new fields, error codes, provider slugs) can ship at any time; removals, renames and changes of meaning are announced here, and by email to every account that holds an API key, at least 90 days ahead. See Versioning and changes.
Launch contract
These changes shipped without notice because the API had not launched yet.Search moved to the apps collection
- Search is now
GET /v1/apps?q=…, the collection whose records areGET /v1/apps/{hostname}./v1/apps/with a trailing slash is the same collection. GET /v1/searchis removed, with no alias or redirect: it answers404 not_foundlike any unknown path.- The page parameter
pis renamedpage.pis no longer read:?q=acme&p=3serves page 1. qis required: a missing or blankqanswers400 query_required(q is required: give a search query) and uses no search. It used to return an empty page.
- New keys:
query, the canonical query Periphery ran;pageSize, always50; andhasMore,truewhen the next page has at least one hit. Stop paging whenhasMoreisfalse. totalCappedis always present,falseunless counting stopped at 1,000,000.totalcomes from a count kept for up to 10 minutes, raised to at least what the page served proves.- A
qthat reads as nothing, such asstatus:2ooalone, returns{"query": "", "page": 1, "pageSize": 50, "hasMore": false, "total": 0, "totalCapped": false, "hits": []}and uses no search: the""query shows the filter was dropped.
- A search hit is now the first part of the app record: both share the same fields, with the same values for the same hostname. See App fields.
- Removed:
hasCredson search hits. UsehasVerifiedCredsandverifiedDetectors, which hits now carry too. - Removed:
emailCounton app records. Countemailsinstead. - Removed:
htmlUrlon app records. The raw page source is what the credential scan ran over, so it held every finding in full; it is no longer served anywhere, console included. - New on search hits:
hasVerifiedCreds,verifiedDetectors, and the signedfaviconUrlandscreenshotUrl, which expire after an hour. - New on both:
scanned,truewhen Periphery captured the page and stored a snapshot. provider,status,title,lastSeenandhasVerifiedCredsare always present on both, andtextalways on app records.statusis0andtitleandtextare""when there is no value.emails,verifiedDetectorsandcredentialsare[]when Periphery looked and found nothing, and omitted when it never looked. They were omitted whenever empty.faviconHashis present whenever Periphery has the favicon, including a hash of0, and omitted otherwise.verifiedDetectorslists each detector once whatever its case, sorted alphabetically ignoring case.lastSeenmeans Periphery’s last visit that did not prove the host dead, whether or not the host answered. It is not the capture time.- An SVG favicon’s
faviconUrldownloads the file when opened directly. Showing it in an<img>is unaffected. - JSON no longer escapes
&,<and>: signed links contain a literal&.
GET /v1/apps/{hostname}answers404 app_not_foundfor hosts Periphery has never visited, as it already did for dead hosts: an app record exists exactly when search can return the app. The console app page does the same.- The hostname is matched case-insensitively, a trailing dot is ignored, and Unicode hostnames are accepted. The response carries the lower-case ASCII form. A hostname that cannot be valid answers
404straight away.
- Every error body carries a stable
codenext to theerrormessage:{"error": "…", "code": "…"}. Switch oncode; messages may change. See Limits and errors. - A failed API key lookup answers
502 unavailablewithRetry-After: 5instead of503. Retry-Afteris sent on429 rate_limitedand429 ip_rate_limited(1 second),502(5),503 busy(2) and503 maintenance(3600).401responses carryWWW-Authenticate.- A method other than
GETon search (/v1/apps), includingHEADandPOST, answers405 method_not_allowedwithAllow: GET, before the key is checked, and uses no search. It used to run the search. On/v1/apps/{hostname},GETandHEADwork as before, and other methods, which used to be served likeGET, answer405too. - A
pagetoo large to be a number, such as99999999999999999999, answers400 page_out_of_range(page is past the last page (20000)) instead of serving page 1. - Unknown paths on the API host, under
/v1or not, including/v1and the removed/v1/search, answer JSON404 not_found. No/v1path answers with a console page or a redirect. - Plain
http://api.periphery.exposedanswers403 https_requiredinstead of redirecting to HTTPS. A key sent that way travelled in clear text: rotate it. - When Periphery is down, the API host answers JSON
502 unavailable; during maintenance, JSON503 maintenancewithRetry-After: 3600. - Every response, errors included, carries
Cache-Control: no-store.
- The 256 limit on
qcounts characters (Unicode code points), not bytes. - A
lastSeen:orfirstSeen:window too large to represent, over about 292 years such aslastSeen:<30501w, is searched as text, as other unreadable filter values are. It used to wrap around to a much shorter window.
- Credential findings only ever come masked. The
textfield and the screenshot show what the page displayed, so a secret printed on screen appears in them as shown.