{"openapi":"3.1.0","info":{"title":"Degrees of Satoshi API","version":"1","summary":"How many transaction hops separate a Bitcoin address from the earliest miners.","description":"Degrees of Satoshi scores a Bitcoin address by its degree: the fewest hops from the early-mining anchor set to that address along directed transaction-graph edges, where each hop is a payment from one address to another. Degree 0 is the anchor set itself; degree 1 was paid directly by an anchor; and so on. An address on chain with no such path has no lineage. The method, its limits, and what the anchor set is are explained at https://degreesofsatoshi.com/methodology.\n\nEvery result is stamped with artifact_version (the immutable graph release that answered it) and methodology (the version of the method). Pass artifact_version back as a query parameter to reproduce a result after the active release moves on.\n\nThe permalink field of a degree result is the URL to cite for that result. The same URL is sent in the Link header with rel=\"cite-as\"; this document is sent with rel=\"service-desc\".\n\nThe guide for AI agents is https://degreesofsatoshi.com/llms.txt.","termsOfService":"https://degreesofsatoshi.com/privacy"},"servers":[{"url":"https://dfs-api.pat-kingsley.workers.dev","description":"This deployment."}],"externalDocs":{"url":"https://degreesofsatoshi.com/methodology","description":"Methodology"},"paths":{"/api/v1":{"get":{"operationId":"index","summary":"Where things are","description":"Links to this document, the methodology, the guide for AI agents, and an example lookup.","responses":{"200":{"description":"The index.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiIndex"}}}}}}},"/api/v1/openapi.json":{"get":{"operationId":"openapi","summary":"This document","description":"The OpenAPI 3.1 description of the public read-only API. Served from code alone; it needs no release data.","responses":{"200":{"description":"This document.","headers":{"Cache-Control":{"description":"public, max-age=3600","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","description":"An OpenAPI 3.1.0 document."}}}}}}},"/api/v1/degree/{address}":{"get":{"operationId":"degree","summary":"Degree of a Bitcoin address","description":"Look up how many directed transaction hops separate an address from the early-mining anchor set, with its lineage path, tier, percentile, and peer standings.\n\nThe response is one of three shapes, told apart by state. Every shape carries permalink (the URL to cite) and is stamped with artifact_version and methodology.\n\nThe public URL is a moving alias for the active release, so responses are marked no-store. Pass ?artifact_version= to read a fixed release.","parameters":[{"$ref":"#/components/parameters/Address"},{"$ref":"#/components/parameters/ArtifactVersion"}],"responses":{"200":{"description":"The result, in one of three states. Also for addresses that are valid but unknown to the release.","headers":{"Link":{"description":"Two web links: the permalink to cite for this result (rel=\"cite-as\") and this document (rel=\"service-desc\").","schema":{"type":"string"},"example":"<https://degreesofsatoshi.com/degree/1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa/>; rel=\"cite-as\", <https://dfs-api.pat-kingsley.workers.dev/api/v1/openapi.json>; rel=\"service-desc\""},"Cache-Control":{"description":"Always no-store: the public URL is a moving alias. Pin artifact_version to get a result that will not change.","schema":{"type":"string"}},"Access-Control-Expose-Headers":{"description":"Lets a browser read the Link header across origins.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DegreeResult"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/api/v1/histogram":{"get":{"operationId":"histogram","summary":"Release histogram","description":"How degrees are spread across every scored address in the release, plus the tier table and the release metadata that stamps each result.","parameters":[{"$ref":"#/components/parameters/ArtifactVersion"}],"responses":{"200":{"description":"The histogram of the active release, or of the pinned artifact_version.","headers":{"Cache-Control":{"description":"Always no-store: the public URL is a moving alias.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Histogram"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/api/v1/tx/{height}/{prefix}":{"get":{"operationId":"transaction","summary":"Resolve a transaction locator","description":"Turns the (txBlock, txPrefix) pair of a path hop into the full transaction id, for deep links to a block explorer. A pair maps to one transaction forever, so the answer is cached permanently.","parameters":[{"$ref":"#/components/parameters/Height"},{"$ref":"#/components/parameters/Prefix"}],"responses":{"200":{"description":"The transaction id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TxLocatorResult"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/ServerError"}}}}},"components":{"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"What went wrong, in plain words."}}},"AvailabilityState":{"type":"string","enum":["available","not_applicable","unavailable_for_artifact"],"description":"\"available\": the comparison is present. \"not_applicable\": the address has no lineage or no history, so there is nothing to compare. \"unavailable_for_artifact\": this release predates the comparison and does not carry it."},"YearMonth":{"type":"object","description":"A calendar month.","required":["year","month"],"properties":{"year":{"type":"integer","minimum":2009},"month":{"type":"integer","minimum":1,"maximum":12}}},"Tier":{"type":"object","description":"A band of degrees from the release tier table.","required":["tier","degrees","min","max","name"],"properties":{"tier":{"type":"integer","description":"Tier number; lower is closer to the anchor set."},"degrees":{"type":"string","description":"Human-readable degree range, for example \"5–7\"."},"min":{"type":"integer","minimum":0,"description":"Lowest degree in the band."},"max":{"type":"integer","minimum":0,"description":"Highest degree in the band."},"name":{"type":["string","null"],"description":"Display name, or null when the release has not named this tier."}}},"PathHop":{"type":"object","description":"One step along the shortest lineage, from this address back toward the anchor set.","required":["key","degree","year"],"properties":{"key":{"type":"string","pattern":"^[0-9a-f]{16}$","description":"Opaque 64-bit key of the parent address, in hex. Addresses along the path are not disclosed; only entities cleared for publication are named."},"degree":{"type":"integer","minimum":0,"description":"Degree of the parent address."},"year":{"type":"integer","minimum":2009,"description":"Year the parent address itself became connected."},"label":{"type":"string","description":"Public label of the parent when it is a known entity cleared for publication. Absent otherwise."},"txBlock":{"type":"integer","minimum":1,"description":"Block height of the transaction that made this hop. Absent when not recorded."},"txPrefix":{"type":"string","pattern":"^[0-9a-f]{16}$","description":"First 8 bytes of that transaction id, in hex. Resolve to a full txid with GET /api/v1/tx/{height}/{prefix}. Absent when not recorded."}}},"Standing":{"type":"object","description":"Where this address stands among its peers. Carried only by releases whose record_schema is lookup-v2-q16.","required":["first_seen_cohort","same_degree_connection"],"properties":{"first_seen_cohort":{"type":"object","required":["year","closer_than","quantized"],"properties":{"year":{"type":"integer","minimum":2009,"description":"The first-seen year this address is compared within."},"closer_than":{"type":"number","minimum":0,"maximum":1,"description":"Share of scored addresses first seen in the same year that this address is closer to the anchor set than (a lower degree)."},"quantized":{"const":true,"description":"Always true: the share was stored as a 16-bit value and rounded down, so it never overstates."}}},"same_degree_connection":{"description":"Null when fewer than 1,000 scored addresses share this degree.","oneOf":[{"type":"object","required":["degree","population","earlier_than","quantized"],"properties":{"degree":{"type":"integer","minimum":0,"description":"The exact degree this address is compared within."},"population":{"type":"integer","minimum":1000,"description":"Number of scored addresses at exactly this degree."},"earlier_than":{"type":"number","minimum":0,"maximum":1,"description":"Share of scored addresses at exactly this degree that became connected strictly later than this one."},"quantized":{"const":true,"description":"Always true: the share was stored as a 16-bit value and rounded down, so it never overstates."}}},{"type":"null"}]}}},"Signals":{"type":"object","description":"Four more peer comparisons over the scored population of the release. Carried only by releases whose signals_schema is lineage-signals-q16-v1.","required":["history_age","same_month_distance","era_connection","same_degree_speed"],"properties":{"history_age":{"type":"object","required":["older_than","population","quantized"],"properties":{"older_than":{"type":"number","minimum":0,"maximum":1,"description":"Share of all scored addresses whose first on-chain appearance is strictly later than this one."},"population":{"type":"integer","minimum":1000,"description":"Number of scored addresses in the release."},"quantized":{"const":true,"description":"Always true: the share was stored as a 16-bit value and rounded down, so it never overstates."}}},"same_month_distance":{"description":"Null when fewer than 1,000 scored addresses were first seen in the same month.","oneOf":[{"type":"object","required":["year","month","closer_than","population","quantized"],"properties":{"year":{"type":"integer","minimum":2009},"month":{"type":"integer","minimum":1,"maximum":12},"closer_than":{"type":"number","minimum":0,"maximum":1,"description":"Share of scored addresses first seen in the same month that have a strictly higher degree than this one."},"population":{"type":"integer","minimum":1000,"description":"Number of scored addresses first seen in that month."},"quantized":{"const":true,"description":"Always true: the share was stored as a 16-bit value and rounded down, so it never overstates."}}},{"type":"null"}]},"era_connection":{"description":"Null when fewer than 1,000 scored addresses were first seen in the same year.","oneOf":[{"type":"object","required":["year","earlier_than","population","quantized"],"properties":{"year":{"type":"integer","minimum":2009},"earlier_than":{"type":"number","minimum":0,"maximum":1,"description":"Share of scored addresses first seen in the same year that became connected strictly later than this one."},"population":{"type":"integer","minimum":1000,"description":"Number of scored addresses first seen in that year."},"quantized":{"const":true,"description":"Always true: the share was stored as a 16-bit value and rounded down, so it never overstates."}}},{"type":"null"}]},"same_degree_speed":{"description":"Null when fewer than 1,000 scored addresses share this degree.","oneOf":[{"type":"object","required":["degree","faster_than","population","quantized"],"properties":{"degree":{"type":"integer","minimum":0},"faster_than":{"type":"number","minimum":0,"maximum":1,"description":"Share of scored addresses at exactly this degree that waited strictly longer between first appearance and connection."},"population":{"type":"integer","minimum":1000,"description":"Number of scored addresses at exactly this degree."},"quantized":{"const":true,"description":"Always true: the share was stored as a 16-bit value and rounded down, so it never overstates."}}},{"type":"null"}]}}},"DegreeResultCommon":{"type":"object","description":"Fields present in every degree result, whatever its state.","required":["address","kind","state","permalink","methodology","artifact_version","tiers_rev","max_degree","statistics_state","statistics_revision","standing","signals_state","signals_revision","signals"],"properties":{"address":{"type":"string","description":"The address the result is for, as the service normalizes it: base58 addresses as given, bech32 and bech32m addresses in lowercase."},"kind":{"type":"string","enum":["P2PKH","P2SH","P2WPKH","P2WSH","P2TR","P2W?"],"description":"Script type read from the address encoding. \"P2W?\" is a witness program of a version the service does not name further."},"state":{"type":"string","enum":["ok","no_lineage","no_onchain_history"],"description":"\"ok\": connected, with a degree. \"no_lineage\": on chain, but no path back to the anchor set. \"no_onchain_history\": valid, but never seen on chain as of this release."},"permalink":{"type":"string","format":"uri","description":"The URL to cite for this result. Also sent as the Link header with rel=\"cite-as\"."},"methodology":{"type":"string","description":"Methodology version that produced the numbers. Explained at https://degreesofsatoshi.com/methodology."},"artifact_version":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,95}$","description":"Immutable graph release that answered this request. Pass it back as ?artifact_version= to reproduce the result after the active release moves on."},"tiers_rev":{"type":"string","description":"Revision of the tier table this release uses."},"max_degree":{"type":"integer","minimum":0,"description":"Highest degree in this release."},"statistics_state":{"$ref":"#/components/schemas/AvailabilityState"},"statistics_revision":{"type":["string","null"],"description":"\"standings-q16-v1\" when the release carries standings, else null."},"standing":{"description":"Present only when statistics_state is \"available\".","oneOf":[{"$ref":"#/components/schemas/Standing"},{"type":"null"}]},"signals_state":{"$ref":"#/components/schemas/AvailabilityState"},"signals_revision":{"type":["string","null"],"description":"\"lineage-signals-q16-v1\" when the release carries signals, else null."},"signals":{"description":"Present only when signals_state is \"available\".","oneOf":[{"$ref":"#/components/schemas/Signals"},{"type":"null"}]}}},"DegreeResultOk":{"description":"The address is connected to the early-mining anchor set.","allOf":[{"$ref":"#/components/schemas/DegreeResultCommon"},{"type":"object","required":["state","degree","degree_display","percentile","tier","connected_since","first_seen","is_anchor","label","path"],"properties":{"state":{"const":"ok"},"degree":{"type":"integer","minimum":0,"description":"Fewest directed hops from the anchor set to this address. 0 means the address is itself in the anchor set."},"degree_display":{"type":"string","description":"The degree as the site shows it: the number itself up to 100, then \"100+\". Use degree for the exact value."},"percentile":{"type":["number","null"],"minimum":0,"maximum":1,"description":"Cumulative share of scored addresses at this degree or closer. Null when the release histogram has no row for this degree."},"tier":{"description":"Null when no tier band covers this degree.","oneOf":[{"$ref":"#/components/schemas/Tier"},{"type":"null"}]},"connected_since":{"allOf":[{"$ref":"#/components/schemas/YearMonth"}],"description":"Month the address first became connected to the anchor set."},"first_seen":{"allOf":[{"$ref":"#/components/schemas/YearMonth"}],"description":"Month the address first appeared on chain."},"is_anchor":{"type":"boolean","description":"True when the address is in the anchor set."},"label":{"type":["string","null"],"description":"Public label when the address belongs to a known entity cleared for publication."},"path":{"type":"array","maxItems":16,"items":{"$ref":"#/components/schemas/PathHop"},"description":"The near end of the shortest lineage, from this address back toward the anchor set: at most 16 hops, ending early at an anchor. Deep chains are cut off, not walked in full."},"statistics_state":{"type":"string","enum":["available","unavailable_for_artifact"]},"signals_state":{"type":"string","enum":["available","unavailable_for_artifact"]}}}]},"DegreeResultNoLineage":{"description":"The address has on-chain history but no path back to the anchor set, so it has no degree.","allOf":[{"$ref":"#/components/schemas/DegreeResultCommon"},{"type":"object","required":["state","degree","degree_display","percentile","tier","connected_since","first_seen","is_anchor","label","path"],"properties":{"state":{"const":"no_lineage"},"degree":{"type":"null"},"degree_display":{"type":"null"},"percentile":{"type":"null"},"tier":{"type":"null"},"connected_since":{"type":"null"},"first_seen":{"allOf":[{"$ref":"#/components/schemas/YearMonth"}],"description":"Month the address first appeared on chain."},"is_anchor":{"type":"boolean","description":"False: anchors always have a degree."},"label":{"type":["string","null"],"description":"Public label when the address belongs to a known entity cleared for publication."},"path":{"type":"array","maxItems":0,"description":"Always empty."},"statistics_state":{"type":"string","enum":["not_applicable","unavailable_for_artifact"],"description":"Never \"available\": there is nothing to compare for this address."},"standing":{"type":"null"},"signals_state":{"type":"string","enum":["not_applicable","unavailable_for_artifact"],"description":"Never \"available\": there is nothing to compare for this address."},"signals":{"type":"null"}}}]},"DegreeResultNoOnchainHistory":{"description":"A valid address that had never appeared on chain when this release was built. Distinct from having no lineage.","allOf":[{"$ref":"#/components/schemas/DegreeResultCommon"},{"type":"object","required":["state"],"properties":{"state":{"const":"no_onchain_history"},"statistics_state":{"type":"string","enum":["not_applicable","unavailable_for_artifact"],"description":"Never \"available\": there is nothing to compare for this address."},"standing":{"type":"null"},"signals_state":{"type":"string","enum":["not_applicable","unavailable_for_artifact"],"description":"Never \"available\": there is nothing to compare for this address."},"signals":{"type":"null"}}}]},"DegreeResult":{"description":"One of three shapes, told apart by state.","oneOf":[{"$ref":"#/components/schemas/DegreeResultOk"},{"$ref":"#/components/schemas/DegreeResultNoLineage"},{"$ref":"#/components/schemas/DegreeResultNoOnchainHistory"}],"discriminator":{"propertyName":"state","mapping":{"ok":"#/components/schemas/DegreeResultOk","no_lineage":"#/components/schemas/DegreeResultNoLineage","no_onchain_history":"#/components/schemas/DegreeResultNoOnchainHistory"}}},"Histogram":{"type":"object","description":"The release histogram: how degrees are spread across every scored address, plus the release metadata that stamps each result. Releases may add fields.","required":["version","methodology","tiers_rev","reached","addresses","no_lineage","max_degree","cumulative","tiers"],"additionalProperties":true,"properties":{"version":{"type":"string","description":"Artifact version of this release; the artifact_version that degree results carry."},"methodology":{"type":"string","description":"Methodology version of this release."},"tiers_rev":{"type":"string","description":"Revision of the tier table."},"record_schema":{"type":"string","description":"Declared lookup record layout. \"lookup-v2-q16\" carries standings. Absent on legacy releases."},"record_bytes":{"type":"integer","description":"Bytes per lookup record (48 for lookup-v2-q16)."},"statistics_revision":{"type":"string","description":"\"standings-q16-v1\" when standings are carried."},"signals_schema":{"type":"string","description":"\"lineage-signals-q16-v1\" when signals are carried."},"signals_record_bytes":{"type":"integer","description":"Bytes per signals record (8)."},"signals_revision":{"type":"string","description":"\"lineage-signals-q16-v1\" when signals are carried."},"signal_populations":{"type":"object","description":"Population sizes behind the signals.","properties":{"scored":{"type":"integer","minimum":0,"description":"Scored addresses in the release."},"first_seen_month":{"type":"object","additionalProperties":{"type":"integer","minimum":0},"description":"Scored addresses per first-seen month, keyed \"YYYY-MM\"."},"first_seen_year":{"type":"object","additionalProperties":{"type":"integer","minimum":0},"description":"Scored addresses per first-seen year, keyed \"YYYY\"."}}},"reached":{"type":"integer","minimum":0,"description":"Scored addresses: those with a degree."},"addresses":{"type":"integer","minimum":0,"description":"All addresses with on-chain history in the release."},"no_lineage":{"type":"integer","minimum":0,"description":"Addresses with history but no path back to the anchor set."},"max_degree":{"type":"integer","minimum":0,"description":"Highest degree in the release."},"as_of_height":{"type":"integer","minimum":0,"description":"Block height the release was built from. Absent on releases that predate the field."},"cumulative":{"type":"object","additionalProperties":{"type":"number","minimum":0,"maximum":1},"description":"Keyed by degree (as a string): cumulative share of scored addresses at that degree or closer. This is where the percentile in a degree result comes from."},"tiers":{"type":"array","items":{"$ref":"#/components/schemas/Tier"},"description":"The tier table, ordered by tier."},"degrees":{"type":"array","description":"Exact count per degree and its share of the scored population. Present on current releases.","items":{"type":"object","required":["degree","count","share_of_scored"],"properties":{"degree":{"type":"integer","minimum":0},"count":{"type":"integer","minimum":0},"share_of_scored":{"type":"number","minimum":0,"maximum":1}}}}}},"TxLocatorResult":{"type":"object","required":["txid"],"properties":{"txid":{"type":"string","pattern":"^[0-9a-f]{64}$","description":"The full transaction id, in hex."}}},"ApiIndex":{"type":"object","required":["name","openapi","methodology","llms","degree_example"],"properties":{"name":{"type":"string"},"openapi":{"type":"string","format":"uri","description":"This document."},"methodology":{"type":"string","format":"uri","description":"How degrees are computed."},"llms":{"type":"string","format":"uri","description":"The site guide for AI agents."},"degree_example":{"type":"string","format":"uri","description":"A degree lookup you can try: the genesis coinbase address."}}}},"parameters":{"Address":{"name":"address","in":"path","required":true,"description":"A Bitcoin mainnet address: base58 P2PKH (1...) or P2SH (3...), or bech32/bech32m (bc1...). Surrounding whitespace is trimmed; bech32 case is normalized.","schema":{"type":"string","minLength":14,"maxLength":90},"example":"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"},"ArtifactVersion":{"name":"artifact_version","in":"query","required":false,"description":"Pin the immutable graph release to read from, as stamped on an earlier result. Omit for the active release. Must match the pattern and must not contain \"..\".","schema":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,95}$"}},"Height":{"name":"height","in":"path","required":true,"description":"Block height the transaction was mined at.","schema":{"type":"integer","minimum":0}},"Prefix":{"name":"prefix","in":"path","required":true,"description":"First 8 bytes of the transaction id, in lowercase hex: the txPrefix of a path hop.","schema":{"type":"string","pattern":"^[0-9a-f]{16}$"}}},"responses":{"BadRequest":{"description":"The address, artifact version, or locator could not be read. The error says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such block, or no transaction in that block begins with the prefix.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"TooManyRequests":{"description":"Rate limited: roughly 30 uncached lookups a minute per client address. Cached results do not count.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"The release could not be read. Retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}