Add calcfi.app 1.0.0 (CalcFi Insights API)#2541
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces the OpenAPI 3.1.0 specification for the CalcFi Insights API, defining endpoints for aggregate data such as calculator runs and score distributions. Feedback suggests pretty-printing the minified JSON for better maintainability, adding missing k-anonymity constraints to the WatchAggregateRow schema, and including standard error responses (400, 500) to complete the API contract.
| @@ -0,0 +1 @@ | |||
| {"openapi":"3.1.0","info":{"title":"CalcFi Insights API","version":"1.0.0","description":"Public, read-only aggregate data feed from CalcFi.app. All rows are k-anonymity safe (count >= 10) and never include PII. Free for non-commercial use; attribution requested. Updated daily at 05:00 UTC.","contact":{"name":"CalcFi","url":"https://calcfi.app"},"license":{"name":"CC BY 4.0","url":"https://creativecommons.org/licenses/by/4.0/"}},"servers":[{"url":"https://calcfi.app","description":"Production"}],"tags":[{"name":"calc","description":"Calculator-run aggregates"},{"name":"distributions","description":"Score/burden distributions"},{"name":"sequences","description":"Calculator co-use patterns"},{"name":"watch","description":"Watch alert aggregates"},{"name":"meta","description":"API metadata"}],"paths":{"/api/insights/calc-aggregates":{"get":{"tags":["calc"],"summary":"Daily calculator-run counts banded by city/state/result","parameters":[{"name":"since","in":"query","description":"Earliest day to include (YYYY-MM-DD, UTC). Defaults to 90 days ago.","schema":{"type":"string","format":"date","example":"2026-04-01"}},{"name":"limit","in":"query","description":"Max rows per page (1-1000). Default 200.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":200}},{"name":"cursor","in":"query","description":"Row offset for pagination — pass `next_cursor` from previous response.","schema":{"type":"integer","minimum":0,"default":0}},{"name":"slug","in":"query","schema":{"type":"string","example":"mortgage-calculator"}},{"name":"city","in":"query","schema":{"type":"string","example":"austin"}},{"name":"state","in":"query","schema":{"type":"string","example":"TX"}},{"name":"band","in":"query","schema":{"type":"string","example":"1.5-2k"}}],"responses":{"200":{"$ref":"#/components/responses/CalcAggregates"}}}},"/api/insights/reality-score-dist":{"get":{"tags":["distributions"],"summary":"Reality-score distribution by city/age/income band","parameters":[{"name":"since","in":"query","description":"Earliest day to include (YYYY-MM-DD, UTC). Defaults to 90 days ago.","schema":{"type":"string","format":"date","example":"2026-04-01"}},{"name":"limit","in":"query","description":"Max rows per page (1-1000). Default 200.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":200}},{"name":"cursor","in":"query","description":"Row offset for pagination — pass `next_cursor` from previous response.","schema":{"type":"integer","minimum":0,"default":0}},{"name":"city","in":"query","schema":{"type":"string"}},{"name":"age_band","in":"query","schema":{"type":"string","example":"26-35"}},{"name":"income_band","in":"query","schema":{"type":"string","example":"50-75k"}},{"name":"score_band","in":"query","schema":{"type":"string","example":"40-60"}}],"responses":{"200":{"$ref":"#/components/responses/RealityScoreDist"}}}},"/api/insights/cost-burden-dist":{"get":{"tags":["distributions"],"summary":"Cost-burden distribution (housing/income ratio bands)","parameters":[{"name":"since","in":"query","description":"Earliest day to include (YYYY-MM-DD, UTC). Defaults to 90 days ago.","schema":{"type":"string","format":"date","example":"2026-04-01"}},{"name":"limit","in":"query","description":"Max rows per page (1-1000). Default 200.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":200}},{"name":"cursor","in":"query","description":"Row offset for pagination — pass `next_cursor` from previous response.","schema":{"type":"integer","minimum":0,"default":0}},{"name":"city","in":"query","schema":{"type":"string"}},{"name":"income_band","in":"query","schema":{"type":"string"}},{"name":"burden_band","in":"query","schema":{"type":"string","example":"30-40%"}}],"responses":{"200":{"$ref":"#/components/responses/CostBurdenDist"}}}},"/api/insights/sequence-patterns":{"get":{"tags":["sequences"],"summary":"Calculator co-use patterns (prev → next slug counts)","parameters":[{"name":"since","in":"query","description":"Earliest day to include (YYYY-MM-DD, UTC). Defaults to 90 days ago.","schema":{"type":"string","format":"date","example":"2026-04-01"}},{"name":"limit","in":"query","description":"Max rows per page (1-1000). Default 200.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":200}},{"name":"cursor","in":"query","description":"Row offset for pagination — pass `next_cursor` from previous response.","schema":{"type":"integer","minimum":0,"default":0}},{"name":"prev_slug","in":"query","schema":{"type":"string"}},{"name":"next_slug","in":"query","schema":{"type":"string"}}],"responses":{"200":{"$ref":"#/components/responses/SequencePatterns"}}}},"/api/insights/watch-aggregates":{"get":{"tags":["watch"],"summary":"Watch-alert pin/fire counts banded by source/threshold","parameters":[{"name":"since","in":"query","description":"Earliest day to include (YYYY-MM-DD, UTC). Defaults to 90 days ago.","schema":{"type":"string","format":"date","example":"2026-04-01"}},{"name":"limit","in":"query","description":"Max rows per page (1-1000). Default 200.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":200}},{"name":"cursor","in":"query","description":"Row offset for pagination — pass `next_cursor` from previous response.","schema":{"type":"integer","minimum":0,"default":0}},{"name":"source","in":"query","schema":{"type":"string","example":"mortgage-rate-30y"}},{"name":"operator","in":"query","schema":{"type":"string","example":"lt"}},{"name":"threshold_band","in":"query","schema":{"type":"string"}}],"responses":{"200":{"$ref":"#/components/responses/WatchAggregates"}}}},"/api/insights/health":{"get":{"tags":["meta"],"summary":"Liveness probe","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"ts":{"type":"string","format":"date-time"},"tables":{"type":"integer"}}}}}}}}}},"components":{"schemas":{"Page":{"type":"object","required":["topic","since","count","next_cursor","rows"],"properties":{"topic":{"type":"string"},"since":{"type":"string","format":"date"},"count":{"type":"integer"},"next_cursor":{"type":["integer","null"]},"rows":{"type":"array","items":{}}}},"CalcAggregateRow":{"type":"object","properties":{"day":{"type":"string","format":"date"},"calc_slug":{"type":"string"},"city":{"type":"string"},"state":{"type":"string"},"result_band":{"type":"string","description":"Banded result bucket — never raw value"},"count_runs":{"type":"integer","minimum":10}}},"RealityScoreRow":{"type":"object","properties":{"day":{"type":"string","format":"date"},"city":{"type":"string"},"age_band":{"type":"string","enum":["18-25","26-35","36-45","46-55","56-65","65+"]},"income_band":{"type":"string","enum":["<25k","25-50k","50-75k","75-100k","100-150k","150-250k","250-500k","500k+"]},"score_band":{"type":"string","enum":["0-20","20-40","40-60","60-80","80-100"]},"count":{"type":"integer","minimum":10}}},"CostBurdenRow":{"type":"object","properties":{"day":{"type":"string","format":"date"},"city":{"type":"string"},"income_band":{"type":"string"},"burden_band":{"type":"string","enum":["<20%","20-30%","30-40%","40-50%","50%+"]},"count":{"type":"integer","minimum":10}}},"SequencePatternRow":{"type":"object","properties":{"day":{"type":"string","format":"date"},"prev_slug":{"type":"string"},"next_slug":{"type":"string"},"count":{"type":"integer","minimum":10}}},"WatchAggregateRow":{"type":"object","properties":{"day":{"type":"string","format":"date"},"source":{"type":"string"},"operator":{"type":"string"},"threshold_band":{"type":"string"},"count_pinned":{"type":"integer"},"count_fired":{"type":"integer"}}}},"responses":{"CalcAggregates":{"description":"Page of calc-run aggregates","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Page"},{"type":"object","properties":{"rows":{"type":"array","items":{"$ref":"#/components/schemas/CalcAggregateRow"}}}}]},"examples":{"mortgageInTexas":{"summary":"Mortgage payment runs in Texas, last 30 days","value":{"topic":"calc-aggregates","since":"2026-04-01","count":3,"next_cursor":null,"rows":[{"day":"2026-04-15","calc_slug":"mortgage-payment","city":"austin","state":"TX","result_band":"2-2.5k","count_runs":18},{"day":"2026-04-15","calc_slug":"mortgage-payment","city":"dallas","state":"TX","result_band":"1.5-2k","count_runs":22},{"day":"2026-04-16","calc_slug":"mortgage-payment","city":"houston","state":"TX","result_band":"1.5-2k","count_runs":14}]}},"paginated":{"summary":"Paginated response with cursor for next page","value":{"topic":"calc-aggregates","since":"2026-02-01","count":200,"next_cursor":200,"rows":[{"day":"2026-04-20","calc_slug":"rent-vs-buy","city":"seattle","state":"WA","result_band":"buy-favored","count_runs":31}]}}}}}},"RealityScoreDist":{"description":"Page of reality-score distribution rows","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Page"},{"type":"object","properties":{"rows":{"type":"array","items":{"$ref":"#/components/schemas/RealityScoreRow"}}}}]},"examples":{"millennialMidIncome":{"summary":"Reality score distribution for 26-35 / $50-75k cohort","value":{"topic":"reality-score-dist","since":"2026-04-01","count":4,"next_cursor":null,"rows":[{"day":"2026-04-15","city":"austin","age_band":"26-35","income_band":"50-75k","score_band":"40-60","count":24},{"day":"2026-04-15","city":"austin","age_band":"26-35","income_band":"50-75k","score_band":"60-80","count":17},{"day":"2026-04-15","city":"denver","age_band":"26-35","income_band":"50-75k","score_band":"40-60","count":12},{"day":"2026-04-16","city":"denver","age_band":"26-35","income_band":"50-75k","score_band":"20-40","count":11}]}}}}}},"CostBurdenDist":{"description":"Page of cost-burden distribution rows","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Page"},{"type":"object","properties":{"rows":{"type":"array","items":{"$ref":"#/components/schemas/CostBurdenRow"}}}}]},"examples":{"austinBurden":{"summary":"Cost-burden distribution for Austin renters/buyers","value":{"topic":"cost-burden-dist","since":"2026-04-01","count":3,"next_cursor":null,"rows":[{"day":"2026-04-15","city":"austin","income_band":"50-75k","burden_band":"30-40%","count":28},{"day":"2026-04-15","city":"austin","income_band":"50-75k","burden_band":"40-50%","count":19},{"day":"2026-04-15","city":"austin","income_band":"75-100k","burden_band":"20-30%","count":22}]}}}}}},"SequencePatterns":{"description":"Page of calculator co-use patterns","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Page"},{"type":"object","properties":{"rows":{"type":"array","items":{"$ref":"#/components/schemas/SequencePatternRow"}}}}]},"examples":{"mortgageThenAffordability":{"summary":"Users who run mortgage-payment commonly run affordability next","value":{"topic":"sequence-patterns","since":"2026-04-01","count":3,"next_cursor":null,"rows":[{"day":"2026-04-15","prev_slug":"mortgage-payment","next_slug":"home-affordability","count":47},{"day":"2026-04-15","prev_slug":"mortgage-payment","next_slug":"rent-vs-buy","count":31},{"day":"2026-04-16","prev_slug":"home-affordability","next_slug":"closing-costs","count":18}]}}}}}},"WatchAggregates":{"description":"Page of watch-alert aggregates","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Page"},{"type":"object","properties":{"rows":{"type":"array","items":{"$ref":"#/components/schemas/WatchAggregateRow"}}}}]},"examples":{"mortgageRateWatch":{"summary":"30-year mortgage rate watch pins and fires","value":{"topic":"watch-aggregates","since":"2026-04-01","count":2,"next_cursor":null,"rows":[{"day":"2026-04-15","source":"mortgage-rate-30y","operator":"lt","threshold_band":"6-6.5%","count_pinned":142,"count_fired":38},{"day":"2026-04-15","source":"mortgage-rate-30y","operator":"lt","threshold_band":"6.5-7%","count_pinned":89,"count_fired":71}]}}}}}}}}} No newline at end of file | |||
There was a problem hiding this comment.
The OpenAPI specification is provided as a single minified line, which makes it difficult to review changes and maintain the file. It is standard practice to store such specifications in a pretty-printed format. Additionally, the WatchAggregateRow schema is missing the minimum: 10 constraint on its count fields (count_pinned and count_fired), which is inconsistent with the k-anonymity policy (count >= 10) described in the API info and enforced in other row schemas.
{
"openapi": "3.1.0",
"info": {
"title": "CalcFi Insights API",
"version": "1.0.0",
"description": "Public, read-only aggregate data feed from CalcFi.app. All rows are k-anonymity safe (count >= 10) and never include PII. Free for non-commercial use; attribution requested. Updated daily at 05:00 UTC.",
"contact": {
"name": "CalcFi",
"url": "https://calcfi.app"
},
"license": {
"name": "CC BY 4.0",
"url": "https://creativecommons.org/licenses/by/4.0/"
}
},
"servers": [
{
"url": "https://calcfi.app",
"description": "Production"
}
],
"tags": [
{
"name": "calc",
"description": "Calculator-run aggregates"
},
{
"name": "distributions",
"description": "Score/burden distributions"
},
{
"name": "sequences",
"description": "Calculator co-use patterns"
},
{
"name": "watch",
"description": "Watch alert aggregates"
},
{
"name": "meta",
"description": "API metadata"
}
],
"paths": {
"/api/insights/calc-aggregates": {
"get": {
"tags": [
"calc"
],
"summary": "Daily calculator-run counts banded by city/state/result",
"parameters": [
{
"name": "since",
"in": "query",
"description": "Earliest day to include (YYYY-MM-DD, UTC). Defaults to 90 days ago.",
"schema": {
"type": "string",
"format": "date",
"example": "2026-04-01"
}
},
{
"name": "limit",
"in": "query",
"description": "Max rows per page (1-1000). Default 200.",
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 1000,
"default": 200
}
},
{
"name": "cursor",
"in": "query",
"description": "Row offset for pagination — pass `next_cursor` from previous response.",
"schema": {
"type": "integer",
"minimum": 0,
"default": 0
}
},
{
"name": "slug",
"in": "query",
"schema": {
"type": "string",
"example": "mortgage-calculator"
}
},
{
"name": "city",
"in": "query",
"schema": {
"type": "string",
"example": "austin"
}
},
{
"name": "state",
"in": "query",
"schema": {
"type": "string",
"example": "TX"
}
},
{
"name": "band",
"in": "query",
"schema": {
"type": "string",
"example": "1.5-2k"
}
}
],
"responses": {
"200": {
"$ref": "#/components/responses/CalcAggregates"
}
}
}
},
"/api/insights/reality-score-dist": {
"get": {
"tags": [
"distributions"
],
"summary": "Reality-score distribution by city/age/income band",
"parameters": [
{
"name": "since",
"in": "query",
"description": "Earliest day to include (YYYY-MM-DD, UTC). Defaults to 90 days ago.",
"schema": {
"type": "string",
"format": "date",
"example": "2026-04-01"
}
},
{
"name": "limit",
"in": "query",
"description": "Max rows per page (1-1000). Default 200.",
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 1000,
"default": 200
}
},
{
"name": "cursor",
"in": "query",
"description": "Row offset for pagination — pass `next_cursor` from previous response.",
"schema": {
"type": "integer",
"minimum": 0,
"default": 0
}
},
{
"name": "city",
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "age_band",
"in": "query",
"schema": {
"type": "string",
"example": "26-35"
}
},
{
"name": "income_band",
"in": "query",
"schema": {
"type": "string",
"example": "50-75k"
}
},
{
"name": "score_band",
"in": "query",
"schema": {
"type": "string",
"example": "40-60"
}
}
],
"responses": {
"200": {
"$ref": "#/components/responses/RealityScoreDist"
}
}
}
},
"/api/insights/cost-burden-dist": {
"get": {
"tags": [
"distributions"
],
"summary": "Cost-burden distribution (housing/income ratio bands)",
"parameters": [
{
"name": "since",
"in": "query",
"description": "Earliest day to include (YYYY-MM-DD, UTC). Defaults to 90 days ago.",
"schema": {
"type": "string",
"format": "date",
"example": "2026-04-01"
}
},
{
"name": "limit",
"in": "query",
"description": "Max rows per page (1-1000). Default 200.",
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 1000,
"default": 200
}
},
{
"name": "cursor",
"in": "query",
"description": "Row offset for pagination — pass `next_cursor` from previous response.",
"schema": {
"type": "integer",
"minimum": 0,
"default": 0
}
},
{
"name": "city",
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "income_band",
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "burden_band",
"in": "query",
"schema": {
"type": "string",
"example": "30-40%"
}
}
],
"responses": {
"200": {
"$ref": "#/components/responses/CostBurdenDist"
}
}
}
},
"/api/insights/sequence-patterns": {
"get": {
"tags": [
"sequences"
],
"summary": "Calculator co-use patterns (prev → next slug counts)",
"parameters": [
{
"name": "since",
"in": "query",
"description": "Earliest day to include (YYYY-MM-DD, UTC). Defaults to 90 days ago.",
"schema": {
"type": "string",
"format": "date",
"example": "2026-04-01"
}
},
{
"name": "limit",
"in": "query",
"description": "Max rows per page (1-1000). Default 200.",
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 1000,
"default": 200
}
},
{
"name": "cursor",
"in": "query",
"description": "Row offset for pagination — pass `next_cursor` from previous response.",
"schema": {
"type": "integer",
"minimum": 0,
"default": 0
}
},
{
"name": "prev_slug",
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "next_slug",
"in": "query",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"$ref": "#/components/responses/SequencePatterns"
}
}
}
},
"/api/insights/watch-aggregates": {
"get": {
"tags": [
"watch"
],
"summary": "Watch-alert pin/fire counts banded by source/threshold",
"parameters": [
{
"name": "since",
"in": "query",
"description": "Earliest day to include (YYYY-MM-DD, UTC). Defaults to 90 days ago.",
"schema": {
"type": "string",
"format": "date",
"example": "2026-04-01"
}
},
{
"name": "limit",
"in": "query",
"description": "Max rows per page (1-1000). Default 200.",
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 1000,
"default": 200
}
},
{
"name": "cursor",
"in": "query",
"description": "Row offset for pagination — pass `next_cursor` from previous response.",
"schema": {
"type": "integer",
"minimum": 0,
"default": 0
}
},
{
"name": "source",
"in": "query",
"schema": {
"type": "string",
"example": "mortgage-rate-30y"
}
},
{
"name": "operator",
"in": "query",
"schema": {
"type": "string",
"example": "lt"
}
},
{
"name": "threshold_band",
"in": "query",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"$ref": "#/components/responses/WatchAggregates"
}
}
}
},
"/api/insights/health": {
"get": {
"tags": [
"meta"
],
"summary": "Liveness probe",
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"ok": {
"type": "boolean"
},
"ts": {
"type": "string",
"format": "date-time"
},
"tables": {
"type": "integer"
}
}
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"Page": {
"type": "object",
"required": [
"topic",
"since",
"count",
"next_cursor",
"rows"
],
"properties": {
"topic": {
"type": "string"
},
"since": {
"type": "string",
"format": "date"
},
"count": {
"type": "integer"
},
"next_cursor": {
"type": [
"integer",
"null"
]
},
"rows": {
"type": "array",
"items": {}
}
}
},
"CalcAggregateRow": {
"type": "object",
"properties": {
"day": {
"type": "string",
"format": "date"
},
"calc_slug": {
"type": "string"
},
"city": {
"type": "string"
},
"state": {
"type": "string"
},
"result_band": {
"type": "string",
"description": "Banded result bucket — never raw value"
},
"count_runs": {
"type": "integer",
"minimum": 10
}
}
},
"RealityScoreRow": {
"type": "object",
"properties": {
"day": {
"type": "string",
"format": "date"
},
"city": {
"type": "string"
},
"age_band": {
"type": "string",
"enum": [
"18-25",
"26-35",
"36-45",
"46-55",
"56-65",
"65+"
]
},
"income_band": {
"type": "string",
"enum": [
"<25k",
"25-50k",
"50-75k",
"75-100k",
"100-150k",
"150-250k",
"250-500k",
"500k+"
]
},
"score_band": {
"type": "string",
"enum": [
"0-20",
"20-40",
"40-60",
"60-80",
"80-100"
]
},
"count": {
"type": "integer",
"minimum": 10
}
}
},
"CostBurdenRow": {
"type": "object",
"properties": {
"day": {
"type": "string",
"format": "date"
},
"city": {
"type": "string"
},
"income_band": {
"type": "string"
},
"burden_band": {
"type": "string",
"enum": [
"<20%",
"20-30%",
"30-40%",
"40-50%",
"50%+"
]
},
"count": {
"type": "integer",
"minimum": 10
}
}
},
"SequencePatternRow": {
"type": "object",
"properties": {
"day": {
"type": "string",
"format": "date"
},
"prev_slug": {
"type": "string"
},
"next_slug": {
"type": "string"
},
"count": {
"type": "integer",
"minimum": 10
}
}
},
"WatchAggregateRow": {
"type": "object",
"properties": {
"day": {
"type": "string",
"format": "date"
},
"source": {
"type": "string"
},
"operator": {
"type": "string"
},
"threshold_band": {
"type": "string"
},
"count_pinned": {
"type": "integer",
"minimum": 10
},
"count_fired": {
"type": "integer",
"minimum": 10
}
}
}
},
"responses": {
"CalcAggregates": {
"description": "Page of calc-run aggregates",
"content": {
"application/json": {
"schema": {
"allOf": [
{
"$ref": "#/components/schemas/Page"
},
{
"type": "object",
"properties": {
"rows": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CalcAggregateRow"
}
}
}
}
]
},
"examples": {
"mortgageInTexas": {
"summary": "Mortgage payment runs in Texas, last 30 days",
"value": {
"topic": "calc-aggregates",
"since": "2026-04-01",
"count": 3,
"next_cursor": null,
"rows": [
{
"day": "2026-04-15",
"calc_slug": "mortgage-payment",
"city": "austin",
"state": "TX",
"result_band": "2-2.5k",
"count_runs": 18
},
{
"day": "2026-04-15",
"calc_slug": "mortgage-payment",
"city": "dallas",
"state": "TX",
"result_band": "1.5-2k",
"count_runs": 22
},
{
"day": "2026-04-16",
"calc_slug": "mortgage-payment",
"city": "houston",
"state": "TX",
"result_band": "1.5-2k",
"count_runs": 14
}
]
}
},
"paginated": {
"summary": "Paginated response with cursor for next page",
"value": {
"topic": "calc-aggregates",
"since": "2026-02-01",
"count": 200,
"next_cursor": 200,
"rows": [
{
"day": "2026-04-20",
"calc_slug": "rent-vs-buy",
"city": "seattle",
"state": "WA",
"result_band": "buy-favored",
"count_runs": 31
}
]
}
}
}
}
}
},
"RealityScoreDist": {
"description": "Page of reality-score distribution rows",
"content": {
"application/json": {
"schema": {
"allOf": [
{
"$ref": "#/components/schemas/Page"
},
{
"type": "object",
"properties": {
"rows": {
"type": "array",
"items": {
"$ref": "#/components/schemas/RealityScoreRow"
}
}
}
}
]
},
"examples": {
"millennialMidIncome": {
"summary": "Reality score distribution for 26-35 / $50-75k cohort",
"value": {
"topic": "reality-score-dist",
"since": "2026-04-01",
"count": 4,
"next_cursor": null,
"rows": [
{
"day": "2026-04-15",
"city": "austin",
"age_band": "26-35",
"income_band": "50-75k",
"score_band": "40-60",
"count": 24
},
{
"day": "2026-04-15",
"city": "austin",
"age_band": "26-35",
"income_band": "50-75k",
"score_band": "60-80",
"count": 17
},
{
"day": "2026-04-15",
"city": "denver",
"age_band": "26-35",
"income_band": "50-75k",
"score_band": "40-60",
"count": 12
},
{
"day": "2026-04-16",
"city": "denver",
"age_band": "26-35",
"income_band": "50-75k",
"score_band": "20-40",
"count": 11
}
]
}
}
}
}
}
},
"CostBurdenDist": {
"description": "Page of cost-burden distribution rows",
"content": {
"application/json": {
"schema": {
"allOf": [
{
"$ref": "#/components/schemas/Page"
},
{
"type": "object",
"properties": {
"rows": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CostBurdenRow"
}
}
}
}
]
},
"examples": {
"austinBurden": {
"summary": "Cost-burden distribution for Austin renters/buyers",
"value": {
"topic": "cost-burden-dist",
"since": "2026-04-01",
"count": 3,
"next_cursor": null,
"rows": [
{
"day": "2026-04-15",
"city": "austin",
"income_band": "50-75k",
"burden_band": "30-40%",
"count": 28
},
{
"day": "2026-04-15",
"city": "austin",
"income_band": "50-75k",
"burden_band": "40-50%",
"count": 19
},
{
"day": "2026-04-15",
"city": "austin",
"income_band": "75-100k",
"burden_band": "20-30%",
"count": 22
}
]
}
}
}
}
}
},
"SequencePatterns": {
"description": "Page of calculator co-use patterns",
"content": {
"application/json": {
"schema": {
"allOf": [
{
"$ref": "#/components/schemas/Page"
},
{
"type": "object",
"properties": {
"rows": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SequencePatternRow"
}
}
}
}
]
},
"examples": {
"mortgageThenAffordability": {
"summary": "Users who run mortgage-payment commonly run affordability next",
"value": {
"topic": "sequence-patterns",
"since": "2026-04-01",
"count": 3,
"next_cursor": null,
"rows": [
{
"day": "2026-04-15",
"prev_slug": "mortgage-payment",
"next_slug": "home-affordability",
"count": 47
},
{
"day": "2026-04-15",
"prev_slug": "mortgage-payment",
"next_slug": "rent-vs-buy",
"count": 31
},
{
"day": "2026-04-16",
"prev_slug": "home-affordability",
"next_slug": "closing-costs",
"count": 18
}
]
}
}
}
}
}
},
"WatchAggregates": {
"description": "Page of watch-alert aggregates",
"content": {
"application/json": {
"schema": {
"allOf": [
{
"$ref": "#/components/schemas/Page"
},
{
"type": "object",
"properties": {
"rows": {
"type": "array",
"items": {
"$ref": "#/components/schemas/WatchAggregateRow"
}
}
}
}
]
},
"examples": {
"mortgageRateWatch": {
"summary": "30-year mortgage rate watch pins and fires",
"value": {
"topic": "watch-aggregates",
"since": "2026-04-01",
"count": 2,
"next_cursor": null,
"rows": [
{
"day": "2026-04-15",
"source": "mortgage-rate-30y",
"operator": "lt",
"threshold_band": "6-6.5%",
"count_pinned": 142,
"count_fired": 38
},
{
"day": "2026-04-15",
"source": "mortgage-rate-30y",
"operator": "lt",
"threshold_band": "6.5-7%",
"count_pinned": 89,
"count_fired": 71
}
]
}
}
}
}
}
}
}
}
}
| @@ -0,0 +1 @@ | |||
| {"openapi":"3.1.0","info":{"title":"CalcFi Insights API","version":"1.0.0","description":"Public, read-only aggregate data feed from CalcFi.app. All rows are k-anonymity safe (count >= 10) and never include PII. Free for non-commercial use; attribution requested. Updated daily at 05:00 UTC.","contact":{"name":"CalcFi","url":"https://calcfi.app"},"license":{"name":"CC BY 4.0","url":"https://creativecommons.org/licenses/by/4.0/"}},"servers":[{"url":"https://calcfi.app","description":"Production"}],"tags":[{"name":"calc","description":"Calculator-run aggregates"},{"name":"distributions","description":"Score/burden distributions"},{"name":"sequences","description":"Calculator co-use patterns"},{"name":"watch","description":"Watch alert aggregates"},{"name":"meta","description":"API metadata"}],"paths":{"/api/insights/calc-aggregates":{"get":{"tags":["calc"],"summary":"Daily calculator-run counts banded by city/state/result","parameters":[{"name":"since","in":"query","description":"Earliest day to include (YYYY-MM-DD, UTC). Defaults to 90 days ago.","schema":{"type":"string","format":"date","example":"2026-04-01"}},{"name":"limit","in":"query","description":"Max rows per page (1-1000). Default 200.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":200}},{"name":"cursor","in":"query","description":"Row offset for pagination — pass `next_cursor` from previous response.","schema":{"type":"integer","minimum":0,"default":0}},{"name":"slug","in":"query","schema":{"type":"string","example":"mortgage-calculator"}},{"name":"city","in":"query","schema":{"type":"string","example":"austin"}},{"name":"state","in":"query","schema":{"type":"string","example":"TX"}},{"name":"band","in":"query","schema":{"type":"string","example":"1.5-2k"}}],"responses":{"200":{"$ref":"#/components/responses/CalcAggregates"}}}},"/api/insights/reality-score-dist":{"get":{"tags":["distributions"],"summary":"Reality-score distribution by city/age/income band","parameters":[{"name":"since","in":"query","description":"Earliest day to include (YYYY-MM-DD, UTC). Defaults to 90 days ago.","schema":{"type":"string","format":"date","example":"2026-04-01"}},{"name":"limit","in":"query","description":"Max rows per page (1-1000). Default 200.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":200}},{"name":"cursor","in":"query","description":"Row offset for pagination — pass `next_cursor` from previous response.","schema":{"type":"integer","minimum":0,"default":0}},{"name":"city","in":"query","schema":{"type":"string"}},{"name":"age_band","in":"query","schema":{"type":"string","example":"26-35"}},{"name":"income_band","in":"query","schema":{"type":"string","example":"50-75k"}},{"name":"score_band","in":"query","schema":{"type":"string","example":"40-60"}}],"responses":{"200":{"$ref":"#/components/responses/RealityScoreDist"}}}},"/api/insights/cost-burden-dist":{"get":{"tags":["distributions"],"summary":"Cost-burden distribution (housing/income ratio bands)","parameters":[{"name":"since","in":"query","description":"Earliest day to include (YYYY-MM-DD, UTC). Defaults to 90 days ago.","schema":{"type":"string","format":"date","example":"2026-04-01"}},{"name":"limit","in":"query","description":"Max rows per page (1-1000). Default 200.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":200}},{"name":"cursor","in":"query","description":"Row offset for pagination — pass `next_cursor` from previous response.","schema":{"type":"integer","minimum":0,"default":0}},{"name":"city","in":"query","schema":{"type":"string"}},{"name":"income_band","in":"query","schema":{"type":"string"}},{"name":"burden_band","in":"query","schema":{"type":"string","example":"30-40%"}}],"responses":{"200":{"$ref":"#/components/responses/CostBurdenDist"}}}},"/api/insights/sequence-patterns":{"get":{"tags":["sequences"],"summary":"Calculator co-use patterns (prev → next slug counts)","parameters":[{"name":"since","in":"query","description":"Earliest day to include (YYYY-MM-DD, UTC). Defaults to 90 days ago.","schema":{"type":"string","format":"date","example":"2026-04-01"}},{"name":"limit","in":"query","description":"Max rows per page (1-1000). Default 200.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":200}},{"name":"cursor","in":"query","description":"Row offset for pagination — pass `next_cursor` from previous response.","schema":{"type":"integer","minimum":0,"default":0}},{"name":"prev_slug","in":"query","schema":{"type":"string"}},{"name":"next_slug","in":"query","schema":{"type":"string"}}],"responses":{"200":{"$ref":"#/components/responses/SequencePatterns"}}}},"/api/insights/watch-aggregates":{"get":{"tags":["watch"],"summary":"Watch-alert pin/fire counts banded by source/threshold","parameters":[{"name":"since","in":"query","description":"Earliest day to include (YYYY-MM-DD, UTC). Defaults to 90 days ago.","schema":{"type":"string","format":"date","example":"2026-04-01"}},{"name":"limit","in":"query","description":"Max rows per page (1-1000). Default 200.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":200}},{"name":"cursor","in":"query","description":"Row offset for pagination — pass `next_cursor` from previous response.","schema":{"type":"integer","minimum":0,"default":0}},{"name":"source","in":"query","schema":{"type":"string","example":"mortgage-rate-30y"}},{"name":"operator","in":"query","schema":{"type":"string","example":"lt"}},{"name":"threshold_band","in":"query","schema":{"type":"string"}}],"responses":{"200":{"$ref":"#/components/responses/WatchAggregates"}}}},"/api/insights/health":{"get":{"tags":["meta"],"summary":"Liveness probe","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"ts":{"type":"string","format":"date-time"},"tables":{"type":"integer"}}}}}}}}}},"components":{"schemas":{"Page":{"type":"object","required":["topic","since","count","next_cursor","rows"],"properties":{"topic":{"type":"string"},"since":{"type":"string","format":"date"},"count":{"type":"integer"},"next_cursor":{"type":["integer","null"]},"rows":{"type":"array","items":{}}}},"CalcAggregateRow":{"type":"object","properties":{"day":{"type":"string","format":"date"},"calc_slug":{"type":"string"},"city":{"type":"string"},"state":{"type":"string"},"result_band":{"type":"string","description":"Banded result bucket — never raw value"},"count_runs":{"type":"integer","minimum":10}}},"RealityScoreRow":{"type":"object","properties":{"day":{"type":"string","format":"date"},"city":{"type":"string"},"age_band":{"type":"string","enum":["18-25","26-35","36-45","46-55","56-65","65+"]},"income_band":{"type":"string","enum":["<25k","25-50k","50-75k","75-100k","100-150k","150-250k","250-500k","500k+"]},"score_band":{"type":"string","enum":["0-20","20-40","40-60","60-80","80-100"]},"count":{"type":"integer","minimum":10}}},"CostBurdenRow":{"type":"object","properties":{"day":{"type":"string","format":"date"},"city":{"type":"string"},"income_band":{"type":"string"},"burden_band":{"type":"string","enum":["<20%","20-30%","30-40%","40-50%","50%+"]},"count":{"type":"integer","minimum":10}}},"SequencePatternRow":{"type":"object","properties":{"day":{"type":"string","format":"date"},"prev_slug":{"type":"string"},"next_slug":{"type":"string"},"count":{"type":"integer","minimum":10}}},"WatchAggregateRow":{"type":"object","properties":{"day":{"type":"string","format":"date"},"source":{"type":"string"},"operator":{"type":"string"},"threshold_band":{"type":"string"},"count_pinned":{"type":"integer"},"count_fired":{"type":"integer"}}}},"responses":{"CalcAggregates":{"description":"Page of calc-run aggregates","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Page"},{"type":"object","properties":{"rows":{"type":"array","items":{"$ref":"#/components/schemas/CalcAggregateRow"}}}}]},"examples":{"mortgageInTexas":{"summary":"Mortgage payment runs in Texas, last 30 days","value":{"topic":"calc-aggregates","since":"2026-04-01","count":3,"next_cursor":null,"rows":[{"day":"2026-04-15","calc_slug":"mortgage-payment","city":"austin","state":"TX","result_band":"2-2.5k","count_runs":18},{"day":"2026-04-15","calc_slug":"mortgage-payment","city":"dallas","state":"TX","result_band":"1.5-2k","count_runs":22},{"day":"2026-04-16","calc_slug":"mortgage-payment","city":"houston","state":"TX","result_band":"1.5-2k","count_runs":14}]}},"paginated":{"summary":"Paginated response with cursor for next page","value":{"topic":"calc-aggregates","since":"2026-02-01","count":200,"next_cursor":200,"rows":[{"day":"2026-04-20","calc_slug":"rent-vs-buy","city":"seattle","state":"WA","result_band":"buy-favored","count_runs":31}]}}}}}},"RealityScoreDist":{"description":"Page of reality-score distribution rows","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Page"},{"type":"object","properties":{"rows":{"type":"array","items":{"$ref":"#/components/schemas/RealityScoreRow"}}}}]},"examples":{"millennialMidIncome":{"summary":"Reality score distribution for 26-35 / $50-75k cohort","value":{"topic":"reality-score-dist","since":"2026-04-01","count":4,"next_cursor":null,"rows":[{"day":"2026-04-15","city":"austin","age_band":"26-35","income_band":"50-75k","score_band":"40-60","count":24},{"day":"2026-04-15","city":"austin","age_band":"26-35","income_band":"50-75k","score_band":"60-80","count":17},{"day":"2026-04-15","city":"denver","age_band":"26-35","income_band":"50-75k","score_band":"40-60","count":12},{"day":"2026-04-16","city":"denver","age_band":"26-35","income_band":"50-75k","score_band":"20-40","count":11}]}}}}}},"CostBurdenDist":{"description":"Page of cost-burden distribution rows","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Page"},{"type":"object","properties":{"rows":{"type":"array","items":{"$ref":"#/components/schemas/CostBurdenRow"}}}}]},"examples":{"austinBurden":{"summary":"Cost-burden distribution for Austin renters/buyers","value":{"topic":"cost-burden-dist","since":"2026-04-01","count":3,"next_cursor":null,"rows":[{"day":"2026-04-15","city":"austin","income_band":"50-75k","burden_band":"30-40%","count":28},{"day":"2026-04-15","city":"austin","income_band":"50-75k","burden_band":"40-50%","count":19},{"day":"2026-04-15","city":"austin","income_band":"75-100k","burden_band":"20-30%","count":22}]}}}}}},"SequencePatterns":{"description":"Page of calculator co-use patterns","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Page"},{"type":"object","properties":{"rows":{"type":"array","items":{"$ref":"#/components/schemas/SequencePatternRow"}}}}]},"examples":{"mortgageThenAffordability":{"summary":"Users who run mortgage-payment commonly run affordability next","value":{"topic":"sequence-patterns","since":"2026-04-01","count":3,"next_cursor":null,"rows":[{"day":"2026-04-15","prev_slug":"mortgage-payment","next_slug":"home-affordability","count":47},{"day":"2026-04-15","prev_slug":"mortgage-payment","next_slug":"rent-vs-buy","count":31},{"day":"2026-04-16","prev_slug":"home-affordability","next_slug":"closing-costs","count":18}]}}}}}},"WatchAggregates":{"description":"Page of watch-alert aggregates","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Page"},{"type":"object","properties":{"rows":{"type":"array","items":{"$ref":"#/components/schemas/WatchAggregateRow"}}}}]},"examples":{"mortgageRateWatch":{"summary":"30-year mortgage rate watch pins and fires","value":{"topic":"watch-aggregates","since":"2026-04-01","count":2,"next_cursor":null,"rows":[{"day":"2026-04-15","source":"mortgage-rate-30y","operator":"lt","threshold_band":"6-6.5%","count_pinned":142,"count_fired":38},{"day":"2026-04-15","source":"mortgage-rate-30y","operator":"lt","threshold_band":"6.5-7%","count_pinned":89,"count_fired":71}]}}}}}}}}} No newline at end of file | |||
Adds the CalcFi Insights API OpenAPI 3.1 spec.
Service:
calcfi.app(free personal-finance calculator platform)Version: 1.0.0
OpenAPI source: https://calcfi.app/api/insights/openapi.json (auto-served, always current)
Documentation: https://calcfi.app/developers
Sample: GET https://calcfi.app/api/insights returns aggregated decision-impact statistics from CalcFi's 300+ calculators.
The API is free, no API key required, served from a public Next.js endpoint on Vercel.
Companion ecosystem:
pip install calcfidata(PyPI · Anaconda)Maintained by Jere Salmisto (ORCID 0009-0000-0916-8684), founder of calcfi.app.