{"openapi":"3.1.0","info":{"title":"WhatPortIs API","version":"1.0.0","summary":"A free, read-only API for the WhatPortIs network port registry.","description":"Look up what runs on a network port: the service, its transports, whether it's\nencrypted by default, how exposed it leaves you, and the long-form technical and\nsecurity write-ups behind it.\n\nNo authentication, no keys, no rate limit. Responses are cacheable and CORS is open,\nso you can call this straight from a browser. Please cache what you fetch.","contact":{"name":"WhatPortIs","url":"https://whatportis.com/api"},"termsOfService":"https://whatportis.com/terms"},"servers":[{"url":"http://localhost:3000","description":"This deployment"}],"tags":[{"name":"ports","description":"The port registry."},{"name":"categories","description":"How the registry is filed."}],"externalDocs":{"description":"API documentation","url":"https://whatportis.com/api"},"paths":{"/api/v1":{"get":{"operationId":"index","summary":"Discover every endpoint from one request.","description":"A machine-readable directory of the API: each endpoint, what it does, and where the OpenAPI document lives. Start here if you're writing a client and want to confirm the surface hasn't moved.","tags":["ports"],"responses":{"200":{"description":"An object listing the API version, endpoints and documentation links.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ApiIndex"}},"required":["data"]}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/ports":{"get":{"operationId":"list-ports","summary":"Filter, sort and page the whole registry.","description":"The workhorse. Every filter below composes — ask for the cleartext UDP services in the well-known range scoring 6 or above and you get exactly that set, with a total so you know how much you're looking at. Records come back in summary form; fetch one by number or slug for the long-form write-ups.","tags":["ports"],"parameters":[{"name":"q","in":"query","required":false,"description":"Free text. Matches a service name, a slug, or the start of a port number — `q=44` finds 44, 443 and 4433.","schema":{"type":"string"},"example":"ssh"},{"name":"number","in":"query","required":false,"description":"Exact port number, 0–65535.","schema":{"type":"integer"},"example":"443"},{"name":"min","in":"query","required":false,"description":"Lowest port number to include, inclusive.","schema":{"type":"integer"},"example":"1024"},{"name":"max","in":"query","required":false,"description":"Highest port number to include, inclusive.","schema":{"type":"integer"},"example":"49151"},{"name":"range","in":"query","required":false,"description":"IANA allocation band: well-known (0–1023), registered (1024–49151) or dynamic (49152–65535). Comma-separated; any of: well-known, registered, dynamic.","schema":{"type":"string"},"example":"well-known"},{"name":"protocol","in":"query","required":false,"description":"Transport the service speaks. A port matches if it speaks any listed transport. Comma-separated; any of: tcp, udp, sctp.","schema":{"type":"string"},"example":"udp"},{"name":"category","in":"query","required":false,"description":"Category slugs. A port matches if it sits in any of them. An unknown slug is an error, not an empty page. Comma-separated.","schema":{"type":"string"},"example":"databases"},{"name":"encrypted","in":"query","required":false,"description":"Whether the service is encrypted by default.","schema":{"type":"boolean"},"example":"true"},{"name":"official","in":"query","required":false,"description":"Whether the assignment is registered with IANA.","schema":{"type":"boolean"},"example":"true"},{"name":"risk","in":"query","required":false,"description":"Risk band: safe (0–2), caution (3–5), risk (6–8), critical (9–10). Comma-separated; any of: safe, caution, risk, critical.","schema":{"type":"string"},"example":"risk,critical"},{"name":"riskMin","in":"query","required":false,"description":"Lowest risk score to include, 0–10, inclusive.","schema":{"type":"integer"},"example":"6"},{"name":"riskMax","in":"query","required":false,"description":"Highest risk score to include, 0–10, inclusive.","schema":{"type":"integer"},"example":"8"},{"name":"sort","in":"query","required":false,"description":"Field to order by. A leading `-` reverses it: `number` is ascending, `-number` descending. `views` is how often a port is looked up on the site.","schema":{"type":"string","enum":["views","-views","number","-number","risk","-risk","name","-name","updated","-updated"],"default":"-views"},"example":"-risk"},{"name":"limit","in":"query","required":false,"description":"How many records to return, 1–100.","schema":{"type":"integer","default":25},"example":"50"},{"name":"offset","in":"query","required":false,"description":"How many records to skip. Combine with `limit` to page; `links.next` does the arithmetic for you.","schema":{"type":"integer","default":0},"example":"25"}],"responses":{"200":{"description":"An array of port summary objects.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PortSummary"}},"meta":{"type":"object","description":"Pagination and query context. Present on collection responses.","additionalProperties":true},"links":{"type":"object","description":"Absolute URLs for the current, next and previous pages. `next` is null on the last page.","properties":{"self":{"type":"string","format":"uri"},"next":{"type":["string","null"],"format":"uri"},"prev":{"type":["string","null"],"format":"uri"}}}},"required":["data"]}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/ports/{port}":{"get":{"operationId":"lookup-port","summary":"Full detail for a port number or a service slug.","description":"The endpoint you want when you have one string from a firewall log or an nmap scan. Pass a number and you get every service registered on it — port 8888 carries six — or pass a slug or exact service name for that one record. Either way `data` is an array, so the response shape never depends on what you typed.","tags":["ports"],"parameters":[{"name":"port","in":"path","required":true,"description":"A port number (0–65535) or a service slug such as `https`. Exact service names work too, case-insensitively.","schema":{"type":"string"},"example":"https"}],"responses":{"200":{"description":"An array of port detail objects — summary fields plus the technical and security write-ups and external references.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Port"}}},"required":["data"]}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/search":{"get":{"operationId":"search","summary":"One string in, best matches first.","description":"Same matching as `q` on the list endpoint, but ranked rather than sorted: an exact number match leads, then an exact name or slug, then prefixes, then anything containing the term — popularity breaks the ties. Use this to power a search box; use `/ports` when you want a filtered set rather than a best guess.","tags":["ports"],"parameters":[{"name":"q","in":"query","required":true,"description":"The term to search for.","schema":{"type":"string"},"example":"sql"},{"name":"limit","in":"query","required":false,"description":"How many records to return, 1–100.","schema":{"type":"integer","default":10},"example":"50"}],"responses":{"200":{"description":"An array of port summary objects, best match first.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PortSummary"}},"meta":{"type":"object","description":"Pagination and query context. Present on collection responses.","additionalProperties":true},"links":{"type":"object","description":"Absolute URLs for the current, next and previous pages. `next` is null on the last page.","properties":{"self":{"type":"string","format":"uri"},"next":{"type":["string","null"],"format":"uri"},"prev":{"type":["string","null"],"format":"uri"}}}},"required":["data"]}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/categories":{"get":{"operationId":"list-categories","summary":"Every category, with how many ports it holds.","description":"Categories are how the registry is filed — web, databases, remote access and so on. The slugs here are what the `category` filter accepts.","tags":["categories"],"responses":{"200":{"description":"An array of category objects.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Category"}}},"required":["data"]}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/categories/{slug}":{"get":{"operationId":"get-category","summary":"One category and the ports filed under it.","description":"The category itself plus a paged list of its ports. Equivalent to filtering `/ports` by that slug, with the category record attached so you don't need a second request to render a heading.","tags":["categories"],"parameters":[{"name":"slug","in":"path","required":true,"description":"The category slug, as returned by `/api/v1/categories`.","schema":{"type":"string"},"example":"databases"},{"name":"sort","in":"query","required":false,"description":"Field to order by. A leading `-` reverses it: `number` is ascending, `-number` descending. `views` is how often a port is looked up on the site.","schema":{"type":"string","enum":["views","-views","number","-number","risk","-risk","name","-name","updated","-updated"],"default":"-views"},"example":"-risk"},{"name":"limit","in":"query","required":false,"description":"How many records to return, 1–100.","schema":{"type":"integer","default":25},"example":"50"},{"name":"offset","in":"query","required":false,"description":"How many records to skip. Combine with `limit` to page; `links.next` does the arithmetic for you.","schema":{"type":"integer","default":0},"example":"25"}],"responses":{"200":{"description":"An object with the `category` and its `ports` — an array of port summaries.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"category":{"$ref":"#/components/schemas/Category"},"ports":{"type":"array","items":{"$ref":"#/components/schemas/PortSummary"}}},"required":["category","ports"]},"meta":{"type":"object","description":"Pagination and query context. Present on collection responses.","additionalProperties":true},"links":{"type":"object","description":"Absolute URLs for the current, next and previous pages. `next` is null on the last page.","properties":{"self":{"type":"string","format":"uri"},"next":{"type":["string","null"],"format":"uri"},"prev":{"type":["string","null"],"format":"uri"}}}},"required":["data"]}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/stats":{"get":{"operationId":"stats","summary":"What the database is made of, counted live.","description":"Aggregate counts across the whole registry: transports, IANA registration, encryption, risk bands and allocation ranges, plus when a record was last corrected. Cheap to poll and useful for dashboards or for sanity-checking a local mirror.","tags":["ports"],"responses":{"200":{"description":"An object of counts and averages.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Stats"}},"required":["data"]}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/openapi.json":{"get":{"operationId":"openapi","summary":"The whole surface as OpenAPI 3.1.","description":"Generate a typed client, import into Postman or Insomnia, or point an agent at it. The document is built from the same definitions this page is rendered from, so it describes the API as deployed.","tags":["ports"],"responses":{"200":{"description":"An OpenAPI 3.1 document.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","description":"An OpenAPI 3.1 document."}},"required":["data"]}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}}},"components":{"schemas":{"PortSummary":{"type":"object","properties":{"number":{"description":"The port number, 0–65535.","type":"integer","minimum":0,"maximum":65535,"example":443},"slug":{"description":"Stable identifier for the service. Unique across the registry and usable as a path segment.","type":"string","example":"https"},"name":{"description":"Service name as it's normally written.","type":"string","example":"HTTPS"},"brief":{"description":"A paragraph describing what runs on the port. Usually plain text, though a few older records still carry inline HTML — sanitise before rendering.","type":"string"},"commonUsage":{"description":"The one-line answer to “what is this for”.","type":"string","example":"Secure web traffic"},"protocols":{"description":"Transports the service speaks, lowercase — some subset of tcp, udp, sctp.","type":"array","items":{"type":"string","enum":["tcp","udp","sctp"]},"example":["tcp"]},"transport":{"description":"The same information as booleans (`tcp`, `udp`, `sctp`), for callers that would rather test a flag than search an array.","type":"object","properties":{"tcp":{"type":"boolean"},"udp":{"type":"boolean"},"sctp":{"type":"boolean"}},"required":["tcp","udp","sctp"]},"official":{"description":"Whether IANA has registered this assignment.","type":"boolean"},"encrypted":{"description":"Whether the service is encrypted by default. Not whether it can be — whether it is, out of the box.","type":"boolean"},"range":{"description":"Which IANA band the number falls in: well-known, registered or dynamic.","type":"string","enum":["well-known","registered","dynamic"]},"risk":{"description":"`score` is 0–10; `level` buckets it as safe, caution, risk or critical. It rates exposure of the service as commonly deployed, not of your particular host.","type":"object","properties":{"score":{"type":"integer","minimum":0,"maximum":10,"example":8},"level":{"type":"string","enum":["safe","caution","risk","critical"]}},"required":["score","level"]},"categories":{"description":"Categories the port is filed under, each with `name` and `slug`.","type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"slug":{"type":"string"}},"required":["name","slug"]}},"views":{"description":"How often the port has been looked up on whatportis.com. The default sort, and a decent proxy for how commonly it's met in the wild.","type":"integer"},"updatedAt":{"description":"When the record last changed, ISO 8601.","type":"string","format":"date-time"},"url":{"description":"The human-readable page for this port.","type":"string","format":"uri"}},"required":["number","slug","name","brief","commonUsage","protocols","transport","official","encrypted","range","risk","categories","views","updatedAt","url"],"description":"A port as returned by any list endpoint."},"Port":{"type":"object","properties":{"number":{"description":"The port number, 0–65535.","type":"integer","minimum":0,"maximum":65535,"example":443},"slug":{"description":"Stable identifier for the service. Unique across the registry and usable as a path segment.","type":"string","example":"https"},"name":{"description":"Service name as it's normally written.","type":"string","example":"HTTPS"},"brief":{"description":"A paragraph describing what runs on the port. Usually plain text, though a few older records still carry inline HTML — sanitise before rendering.","type":"string"},"commonUsage":{"description":"The one-line answer to “what is this for”.","type":"string","example":"Secure web traffic"},"protocols":{"description":"Transports the service speaks, lowercase — some subset of tcp, udp, sctp.","type":"array","items":{"type":"string","enum":["tcp","udp","sctp"]},"example":["tcp"]},"transport":{"description":"The same information as booleans (`tcp`, `udp`, `sctp`), for callers that would rather test a flag than search an array.","type":"object","properties":{"tcp":{"type":"boolean"},"udp":{"type":"boolean"},"sctp":{"type":"boolean"}},"required":["tcp","udp","sctp"]},"official":{"description":"Whether IANA has registered this assignment.","type":"boolean"},"encrypted":{"description":"Whether the service is encrypted by default. Not whether it can be — whether it is, out of the box.","type":"boolean"},"range":{"description":"Which IANA band the number falls in: well-known, registered or dynamic.","type":"string","enum":["well-known","registered","dynamic"]},"risk":{"description":"`score` is 0–10; `level` buckets it as safe, caution, risk or critical. It rates exposure of the service as commonly deployed, not of your particular host.","type":"object","properties":{"score":{"type":"integer","minimum":0,"maximum":10,"example":8},"level":{"type":"string","enum":["safe","caution","risk","critical"]}},"required":["score","level"]},"categories":{"description":"Categories the port is filed under, each with `name` and `slug`.","type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"slug":{"type":"string"}},"required":["name","slug"]}},"views":{"description":"How often the port has been looked up on whatportis.com. The default sort, and a decent proxy for how commonly it's met in the wild.","type":"integer"},"updatedAt":{"description":"When the record last changed, ISO 8601.","type":"string","format":"date-time"},"url":{"description":"The human-readable page for this port.","type":"string","format":"uri"},"description":{"description":"Long-form markdown: `technical` covers how the protocol works, `security` covers vulnerabilities and mitigations. Either may be null where nobody has written it yet.","type":"object","properties":{"technical":{"type":["string","null"]},"security":{"type":["string","null"]}},"required":["technical","security"]},"references":{"description":"External sources — RFCs, vendor docs — each with `title` and `url`.","type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"url":{"type":"string","format":"uri"}},"required":["title","url"]}},"createdAt":{"description":"When the record was first added, ISO 8601.","type":"string","format":"date-time"}},"required":["number","slug","name","brief","commonUsage","protocols","transport","official","encrypted","range","risk","categories","views","updatedAt","url","description","references","createdAt"],"description":"A port with its long-form write-ups, as returned when fetched by number or slug."},"Category":{"type":"object","properties":{"slug":{"description":"Identifier accepted by the `category` filter.","type":"string","example":"databases"},"name":{"description":"Display name.","type":"string","example":"Databases"},"description":{"description":"What belongs in this category, where it's been written.","type":["string","null"]},"portCount":{"description":"How many ports are filed under it.","type":"integer"},"url":{"description":"The human-readable page for this category.","type":"string","format":"uri"}},"required":["slug","name","description","portCount","url"],"description":"A category and how many ports it holds."},"Stats":{"type":"object","description":"Aggregate counts across the registry.","properties":{"ports":{"type":"integer"},"categories":{"type":"integer"},"transport":{"type":"object","properties":{"tcp":{"type":"integer"},"udp":{"type":"integer"},"sctp":{"type":"integer"}}},"registration":{"type":"object","properties":{"official":{"type":"integer"},"unofficial":{"type":"integer"}}},"encryption":{"type":"object","properties":{"encrypted":{"type":"integer"},"cleartext":{"type":"integer"}}},"risk":{"type":"object","properties":{"safe":{"type":"integer"},"caution":{"type":"integer"},"risk":{"type":"integer"},"critical":{"type":"integer"},"average":{"type":"number"}}},"ranges":{"type":"object","properties":{"well-known":{"type":"integer"},"registered":{"type":"integer"},"dynamic":{"type":"integer"}}},"updatedAt":{"type":["string","null"],"format":"date-time"}}},"ApiIndex":{"type":"object","description":"The endpoint directory returned by /api/v1.","additionalProperties":true},"ErrorEnvelope":{"type":"object","properties":{"error":{"type":"object","properties":{"status":{"type":"integer","example":400},"code":{"type":"string","enum":["invalid_parameter","not_found","method_not_allowed","internal_error"]},"message":{"type":"string"},"param":{"type":"string","description":"The parameter at fault, when there is one."},"hint":{"type":"string","description":"How to fix it, when we can say."},"docs":{"type":"string","format":"uri"}},"required":["status","code","message","docs"]}},"required":["error"]}},"parameters":{}},"x-api-version":"1","x-pagination":{"style":"limit/offset","defaultLimit":25,"maxLimit":100}}