{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "# MuniSpot API Quickstart\n\n> **Open in Colab:** [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/OWNER/REPO/blob/main/munispot-quickstart.ipynb)\n> \n> _Replace `OWNER/REPO` with the public quickstart repo once chosen. Until then, upload this file to Colab via **File → Upload notebook**._\n\nStandardized ACFR-derived financials, MuniScore™ ratings, and economic indicators for U.S. municipalities. This notebook takes you from an API key to dataframes and a chart in a few cells.\n\n**You need:** a MuniSpot API key. The financials endpoint requires a Pro or Enterprise key (free-tier keys get a 403 there). Get one at https://www.munispot.com/account/api-keys"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## 1. Setup\n\nInstall dependencies and enter your key. The key is read with `getpass`, so it is **never written into the notebook**."
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "%pip install --quiet requests pandas matplotlib"
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "import getpass\nimport requests\nimport pandas as pd\n\nBASE_URL = \"https://www.munispot.com\"\nAPI_KEY = getpass.getpass(\"Paste your MuniSpot API key (hidden): \").strip()\n\nsession = requests.Session()\nsession.headers.update({\"Authorization\": f\"Bearer {API_KEY}\"})\n\ndef get(path, **params):\n    # GET a MuniSpot API path and return parsed JSON, raising on non-200.\n    r = session.get(f\"{BASE_URL}{path}\", params=params, timeout=30)\n    if r.status_code != 200:\n        raise RuntimeError(f\"{r.status_code} {r.reason}: {r.text[:300]}\")\n    return r.json()\n\nprint(\"Ready. Base:\", BASE_URL)"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## 2. List entities\n\nFilters: `q` (name search), `state`, `sector` (`city`, `county`, `school_district`, `state`). Results are paginated (`per_page` up to 50)."
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "res = get(\"/api/v1/entities\", state=\"CA\", sector=\"city\", per_page=10, fiscal_year=2023)\nentities = pd.DataFrame(res[\"data\"])\nprint(f\"{res['meta']['total']} entities match; showing {len(entities)}\")\nentities"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## 3. Entity detail\n\nOne entity with its flattened MuniScore components for a fiscal year. Swap `EIN` for any value from the list above."
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "EIN = \"366005820\"  # City of Chicago, IL\ndetail = get(f\"/api/v1/entities/{EIN}\", fiscal_year=2023)[\"data\"]\n{k: detail.get(k) for k in (\"ein\", \"name\", \"state\", \"sector\", \"population\", \"overall_score\")}"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## 4. Scores\n\nThe MuniScore breaks down into four weighted components (economy, budgetary, reserves, debt) plus the underlying metric values."
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "scores = get(f\"/api/v1/entities/{EIN}/scores\", fiscal_year=2023)[\"data\"]\nprint(\"Overall MuniScore:\", scores[\"overall_score\"], \"| confidence:\", scores[\"confidence_badge\"])\npd.DataFrame(scores[\"components\"]).T  # economy / budgetary / reserves / debt"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## 5. Financials\n\nRaw standardized line items. `fiscal_year` is required. Each row carries `class_2` (broad category) and `class_1` (specific line). `column_index == 1` is the General Fund; `-1` is Total Governmental Funds. See the Data Dictionary in the API docs for the full classification reference."
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "fin = get(f\"/api/v1/entities/{EIN}/financials\", fiscal_year=2023, statement_type=\"income_statement\")\nitems = pd.DataFrame(fin[\"data\"][\"line_items\"])\nprint(\"line items:\", fin[\"meta\"][\"count\"], \"| truncated:\", fin[\"meta\"][\"truncated\"])\nitems[[\"statement_type\", \"class_2\", \"class_1\", \"reported_stat\", \"value\", \"column_index\"]].head(20)"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## 6. A quick chart\n\nTop General-Fund revenue categories. Filtering on `column_index == 1` keeps numerator and denominator in the same fund."
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "import matplotlib.pyplot as plt\n\nrev = items[(items[\"class_2\"] == \"REVENUES\") & (items[\"column_index\"] == 1)]\nrev = rev.groupby(\"class_1\")[\"value\"].sum().sort_values(ascending=False).head(10)\nax = rev.plot(kind=\"barh\")\nax.invert_yaxis()\nax.set_title(f\"{detail['name']}: top revenue categories (FY2023, General Fund)\")\nax.set_xlabel(\"USD\")\nplt.tight_layout()\nplt.show()"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## Next steps\n\n- Full endpoint reference and a live “Try it” console: https://www.munispot.com/api-docs\n- Evaluating MuniSpot against your current provider? Open **munispot-data-quality-audit.ipynb** for a side-by-side coverage, freshness, line-item match-rate, and score-correlation harness.\n- Prefer natural language? The same data is available via the MuniSpot MCP server (https://www.munispot.com/mcp) for Claude Desktop, Cursor, and Gemini CLI."
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.x"
  },
  "colab": {
   "provenance": []
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}