> ## Documentation Index
> Fetch the complete documentation index at: https://docs.axiomancer.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Discovery & Intelligence

> 12 endpoints for location discovery, cross-metro look-alike cells, nearby intelligence, trends, tier distribution, top movers, port-risk propagation, and government bids.

## `GET` `/api/discover`

Filter locations by score thresholds across all metros.

**Query parameters**

| Name           | Type   | Required | Default | Description                |
| -------------- | ------ | -------- | ------- | -------------------------- |
| `metro`        | string |          |         | Metro slug filter.         |
| `compositeMin` | int    |          |         | Minimum composite score.   |
| `businessMin`  | int    |          |         | Minimum Business Vitality. |
| `safetyMin`    | int    |          |         | Minimum Safety score.      |
| `limit`        | int    |          | `50`    | Max results.               |

**Example**

```bash theme={null}
curl "https://axiomlocus.io/api/discover?metro=sf&compositeMin=60&safetyMin=50"
```

**Response**

```json theme={null}
{ "total": 12, "results": [{ "h3_index": "...", "metro_slug": "sf", "location_name": "Mission District", "composite": 74 }] }
```

| Field                     | Type   | Description                  |
| ------------------------- | ------ | ---------------------------- |
| `total`                   | number | Number of matching locations |
| `results[].composite`     | number | Composite score 0-100        |
| `results[].location_name` | string | Neighborhood name            |

## `POST` `/api/cells/similar`

Find the nearest look-alike cells to a given H3 cell by driver-profile or satellite-image signature. Unlike the same-metro "Comparable cells" section of the [Explorer](/locus/explorer#look-alike-cells) — which ranks by absolute composite-score distance within one metro — this endpoint ranks by *shape* similarity (cosine nearest-neighbor over a per-cell vector) and can look across every scored metro. Use it when you want to answer "where else does a place like this exist?"

Two similarity bases are supported today, plus a blend of the two:

| Mode              | Vector                                                                         | Meaning                                                                                                                                                 | Availability                                                                                                |
| ----------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `score` (default) | 8-d cell score profile (the eight [signal groups](/locus/scoring-conventions)) | Two cells are similar when their signal *mix* looks alike — for example, "high business vitality, weak safety" — regardless of overall composite level. | Live for every scored cell.                                                                                 |
| `satellite`       | 64-d AlphaEarth satellite embedding                                            | Two cells are similar when their satellite-image signature looks alike — built form, greenness, land cover. Independent of the score.                   | Dormant until Earth Engine backfill lands. Returns `no_satellite_embedding` for cells without an embedding. |
| `blend`           | Average of the score and satellite distances                                   | Balances the two views.                                                                                                                                 | Same availability as `satellite` — both sides must be populated for the source and each candidate.          |

**Request body**

| Name            | Type   | Required | Default | Description                                                                                                           |
| --------------- | ------ | -------- | ------- | --------------------------------------------------------------------------------------------------------------------- |
| `h3Index`       | string | ✓        |         | The source H3 cell to look up neighbors for.                                                                          |
| `mode`          | string |          | `score` | Similarity basis: `score`, `satellite`, or `blend`.                                                                   |
| `limit`         | int    |          | `12`    | Max neighbors to return. Clamped to `[1, 100]`.                                                                       |
| `sameMetroOnly` | bool   |          | `false` | Restrict candidates to the source cell's metro. Off by default — the point of this endpoint is cross-metro discovery. |

**Example**

```bash theme={null}
curl -X POST "https://axiomlocus.io/api/cells/similar" \
  -H "content-type: application/json" \
  -d '{ "h3Index": "882a100d63fffff", "mode": "score", "limit": 5 }'
```

**Response (found)**

```json theme={null}
{
  "found": true,
  "sourceH3": "882a100d63fffff",
  "sourceMetro": "sf",
  "cells": [
    {
      "h3_index": "882a306833fffff",
      "metro_slug": "nyc",
      "lat": 40.7128,
      "lng": -74.0060,
      "composite": 71.2,
      "confidence": 0.86,
      "similarity": 0.972,
      "business_vitality": 78,
      "population_momentum": 65,
      "demographics": 70,
      "economic_strength": 74,
      "development_pipeline": 62,
      "accessibility": 88,
      "safety_environment": 55,
      "amenity_demand": 81
    }
  ]
}
```

**Response (source has no satellite embedding, satellite/blend mode)**

```json theme={null}
{
  "found": false,
  "reason": "no_satellite_embedding",
  "sourceH3": "882a100d63fffff",
  "sourceMetro": "sf",
  "cells": []
}
```

| Field                                                                         | Type   | Description                                                                                                                                           |
| ----------------------------------------------------------------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `found`                                                                       | bool   | `false` when the source cell is unknown, has an all-zero score profile, or (satellite/blend) has no embedding.                                        |
| `reason`                                                                      | string | Populated when `found=false` to explain why. `no_satellite_embedding` is the common case today — the cell is scored, but has no satellite vector yet. |
| `sourceH3`                                                                    | string | Echo of the requested H3 index.                                                                                                                       |
| `sourceMetro`                                                                 | string | Metro slug of the source cell, or `null` if the cell is unscored.                                                                                     |
| `cells[].similarity`                                                          | number | Cosine similarity to the source cell, in `[0, 1]`. `1.0` means an identical profile; `0.97` and above is a very tight match.                          |
| `cells[].composite`, `cells[].confidence`, and the eight signal-group columns | number | The neighbor's current score row, so you can render a preview without a follow-up call.                                                               |

<Note>
  **Availability.** `score` mode is live: `feature_vec` is refreshed automatically as cells re-score. `satellite` and `blend` are gated on the AlphaEarth Earth Engine backfill — until embeddings exist, requests in those modes will return `found: false` with `reason: "no_satellite_embedding"`. Score mode is the correct default for production use today.
</Note>

<Tip>
  Set `sameMetroOnly: true` if you want a within-metro comparison — for example, "find other cells in San Francisco that look like this one." Leaving it `false` (the default) is what makes this different from the Explorer's legacy same-metro comparables card.
</Tip>

## `GET` `/api/nearby`

Everything we know near a location — permits, POIs, schools, clinical trials, FDA events, zoning.

**Query parameters**

| Name  | Type  | Required | Default | Description |
| ----- | ----- | -------- | ------- | ----------- |
| `lat` | float | ✓        |         | Latitude.   |
| `lng` | float | ✓        |         | Longitude.  |

**Example**

```bash theme={null}
curl "https://axiomlocus.io/api/nearby?lat=37.7749&lng=-122.4194"
```

**Response**

```json theme={null}
{ "summary": { "permits": 45, "pois": 120, "schools": 3, "clinical_trials": 8 }, "recent": { ... } }
```

| Field     | Type   | Description                                 |
| --------- | ------ | ------------------------------------------- |
| `summary` | object | Count of records per data type within \~1km |
| `recent`  | object | Recent records from each data type          |

## `GET` `/api/metro-tier-distribution`

Count of scored cells in each safety tier bucket for a given signal across a metro. Powers the tier-mix bar on the [Explorer Intelligence Rail](/locus/explorer#metro-key-stats) so you can see at a glance whether a metro is mostly safe or mostly elevated, without leaving the page.

Buckets follow the standard composite score breakpoints: **Prime ≥80**, **Strong 60–79**, **Solid 40–59**, **Watch 20–39**, **Elevated \<20**. Cells with a `null` value for the requested signal are excluded.

**Query parameters**

| Name     | Type   | Required | Default              | Description                                                                                                                                                                                                |
| -------- | ------ | -------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `metro`  | string | ✓        |                      | Metro slug (e.g. `sf`, `nyc`, `denver`).                                                                                                                                                                   |
| `signal` | string |          | `safety_environment` | Column to bucket on. One of `composite`, `business_vitality`, `population_momentum`, `demographics`, `economic_strength`, `development_pipeline`, `accessibility`, `safety_environment`, `amenity_demand`. |

**Example**

```bash theme={null}
curl "https://axiomlocus.io/api/metro-tier-distribution?metro=sf&signal=safety_environment"
```

**Response**

```json theme={null}
{
  "metro": "sf",
  "signal": "safety_environment",
  "total": 1284,
  "buckets": {
    "prime": 412,
    "strong": 537,
    "solid": 246,
    "watch": 71,
    "elevated": 18
  }
}
```

| Field              | Type   | Description                                                                                               |
| ------------------ | ------ | --------------------------------------------------------------------------------------------------------- |
| `metro`            | string | Metro slug echoed from the request.                                                                       |
| `signal`           | string | Signal column the distribution is computed over.                                                          |
| `total`            | number | Total scored cells included in the distribution (cells with `null` on the requested signal are excluded). |
| `buckets.prime`    | number | Cells scoring ≥ 80.                                                                                       |
| `buckets.strong`   | number | Cells scoring 60–79.                                                                                      |
| `buckets.solid`    | number | Cells scoring 40–59.                                                                                      |
| `buckets.watch`    | number | Cells scoring 20–39.                                                                                      |
| `buckets.elevated` | number | Cells scoring \< 20.                                                                                      |

The same endpoint backs distribution bars for any signal group — pass a different `signal` to render a development-pipeline mix, business-vitality mix, or composite mix without a separate route.

## `GET` `/api/score-trends`

Historical score trends for a metro or specific H3 cell. Pass `metro` for a metro-wide aggregate trend or `h3_index` for a per-cell time-series with all eight signal-group sub-scores. Exactly one of the two is required.

The endpoint reads from the `score_history` table that the Railway scorer snapshots once per day, so a fresh row appears for every `(h3_index, profile, snapshot_date)` covered by that day's scorer pass. Cells the scorer didn't touch on a given day will be absent from the time-series for that date.

**Query parameters**

| Name       | Type   | Required | Default | Description                                                                  |
| ---------- | ------ | -------- | ------- | ---------------------------------------------------------------------------- |
| `metro`    | string |          |         | Metro slug for an aggregate metro-wide trend.                                |
| `h3_index` | string |          |         | Specific H3 cell for a detailed per-cell trend with signal-group breakdowns. |
| `days`     | int    |          | `90`    | Lookback period in days.                                                     |

**Metro aggregate example**

```bash theme={null}
curl "https://axiomlocus.io/api/score-trends?metro=sf&days=30"
```

```json theme={null}
{ "metro": "sf", "days": 30, "trend": [{ "date": "2026-03-30", "avg_composite": 42, "cells": 20 }] }
```

| Field                   | Type   | Description                                            |
| ----------------------- | ------ | ------------------------------------------------------ |
| `trend[].date`          | string | Snapshot date (`YYYY-MM-DD`)                           |
| `trend[].avg_composite` | number | Average composite score across the metro for that date |
| `trend[].cells`         | number | Cells in the metro with a snapshot on that date        |

**Per-cell example**

```bash theme={null}
curl "https://axiomlocus.io/api/score-trends?h3_index=882a1072d3fffff&days=90"
```

```json theme={null}
{
  "h3_index": "882a1072d3fffff",
  "days": 90,
  "snapshots": [
    {
      "snapshot_date": "2026-04-29",
      "composite": 71,
      "business_vitality": 68,
      "population_momentum": 74,
      "demographics": 65,
      "economic_strength": 80,
      "development_pipeline": 77,
      "accessibility": 62,
      "safety_environment": 70,
      "amenity_demand": 64
    }
  ]
}
```

| Field                                                            | Type           | Description                                                                            |
| ---------------------------------------------------------------- | -------------- | -------------------------------------------------------------------------------------- |
| `snapshots[].snapshot_date`                                      | string         | Snapshot date (`YYYY-MM-DD`)                                                           |
| `snapshots[].composite`                                          | number         | Cell composite score 0–100 on that date                                                |
| `snapshots[].business_vitality` ... `snapshots[].amenity_demand` | number \| null | Per-group sub-score 0–100; `null` if the group could not be computed for that snapshot |

## `GET` `/api/top-movers`

Discover the cells whose composite score has changed the most over a 7-, 30-, 90-, or 180-day window. Each row reports the latest composite, the prior-window composite, and the delta between them, so you can surface neighborhoods that are heating up (or cooling off) without manually diffing snapshots from [`/api/score-trends`](#get-apiscore-trends).

Deltas are computed against the freshest snapshot in `score_history` at or before the lookback boundary, not a fixed calendar date. This means partial scorer coverage on any given day does not bias the leaderboard — cells without a baseline snapshot inside the window are simply excluded. The same RPC powers the metro-overview "movement" rail in the Explorer and the standalone `/movers` page in the dashboard.

**Query parameters**

| Name        | Type   | Required | Default | Description                                                                                 |
| ----------- | ------ | -------- | ------- | ------------------------------------------------------------------------------------------- |
| `days`      | int    |          | `30`    | Lookback window. Must be one of `7`, `30`, `90`, or `180`.                                  |
| `direction` | string |          | `up`    | `up` for biggest gainers, `down` for biggest decliners, or `all` to rank by absolute delta. |
| `metro`     | string |          |         | Metro slug filter (e.g. `sf`, `nyc`, `boston`). Omit for cross-metro results.               |
| `limit`     | int    |          | `25`    | Max results (1–100).                                                                        |

**Example**

```bash theme={null}
curl "https://axiomlocus.io/api/top-movers?days=30&direction=up&metro=boston&limit=10"
```

**Response**

```json theme={null}
{
  "window_days": 30,
  "direction": "up",
  "metro": "boston",
  "movers": [
    {
      "h3_index": "882a306665fffff",
      "metro_slug": "boston",
      "location_name": "Seaport District",
      "latest_composite": 61,
      "prior_composite": 38,
      "delta": 23,
      "latest_snapshot_date": "2026-04-29",
      "prior_snapshot_date": "2026-03-30"
    }
  ]
}
```

| Field                           | Type           | Description                                                                                         |
| ------------------------------- | -------------- | --------------------------------------------------------------------------------------------------- |
| `window_days`                   | int            | Lookback window echoed from the request.                                                            |
| `direction`                     | string         | `up`, `down`, or `all` — echoed from the request.                                                   |
| `metro`                         | string \| null | Metro slug filter, or `null` when the call was cross-metro.                                         |
| `movers`                        | array          | Cells ranked by composite delta. Empty when the window has no eligible snapshot pairs yet.          |
| `movers[].h3_index`             | string         | H3 cell index.                                                                                      |
| `movers[].metro_slug`           | string         | Metro the cell belongs to.                                                                          |
| `movers[].location_name`        | string         | Neighborhood name.                                                                                  |
| `movers[].latest_composite`     | number         | Composite score 0–100 from the most recent snapshot.                                                |
| `movers[].prior_composite`      | number         | Composite score 0–100 from the freshest snapshot at or before the lookback boundary.                |
| `movers[].delta`                | number         | `latest_composite - prior_composite`. Positive when the cell is improving, negative when declining. |
| `movers[].latest_snapshot_date` | string         | Date of the latest snapshot (`YYYY-MM-DD`).                                                         |
| `movers[].prior_snapshot_date`  | string         | Date of the prior-side snapshot used as the baseline.                                               |

<Note>
  Daily snapshots accumulate from the moment a cell is first scored, so newer metros may return an empty `movers` array on longer windows until enough history has built up. Use a shorter window (`days=7`) to surface activity earlier.
</Note>

## `GET` `/api/schools`

Nearby school quality ratings (1-10 scale).

**Query parameters**

| Name     | Type  | Required | Default | Description      |
| -------- | ----- | -------- | ------- | ---------------- |
| `lat`    | float | ✓        |         | Latitude.        |
| `lng`    | float | ✓        |         | Longitude.       |
| `radius` | float |          | `2`     | Radius in miles. |
| `limit`  | int   |          | `10`    | Max results.     |

**Example**

```bash theme={null}
curl "https://axiomlocus.io/api/schools?lat=37.7749&lng=-122.4194&radius=1"
```

**Response**

```json theme={null}
{ "total": 5, "schools": [{ "name": "Lincoln Elementary", "rating": 8, "enrollment": 450 }] }
```

| Field                  | Type   | Description        |
| ---------------------- | ------ | ------------------ |
| `schools[].rating`     | number | Quality score 1-10 |
| `schools[].enrollment` | number | Student enrollment |

## `GET` `/api/life-sciences`

Clinical trials and FDA enforcement data by state or sponsor.

**Query parameters**

| Name      | Type   | Required | Default | Description                   |
| --------- | ------ | -------- | ------- | ----------------------------- |
| `state`   | string |          |         | US state name.                |
| `sponsor` | string |          |         | Sponsor name (partial match). |
| `type`    | string |          | `all`   | Filter: trials, fda, or all.  |
| `limit`   | int    |          | `50`    | Max results.                  |

**Example**

```bash theme={null}
curl "https://axiomlocus.io/api/life-sciences?state=Massachusetts&type=trials&limit=10"
```

**Response**

```json theme={null}
{ "trials": { "total": 10, "data": [{ "nct_id": "NCT05754281", "sponsor": "Joslin Diabetes Center" }] } }
```

| Field                   | Type   | Description                 |
| ----------------------- | ------ | --------------------------- |
| `trials.data[].nct_id`  | string | ClinicalTrials.gov trial ID |
| `trials.data[].sponsor` | string | Lead sponsor organization   |

## `GET` `/api/zoning`

Zoning district rules — allowed uses, height limits, FAR, setbacks.

**Query parameters**

| Name       | Type   | Required | Default | Description                                              |
| ---------- | ------ | -------- | ------- | -------------------------------------------------------- |
| `metro`    | string | ✓        |         | Metro slug.                                              |
| `code`     | string |          |         | Specific district code.                                  |
| `category` | string |          |         | Filter: residential, commercial, industrial, mixed\_use. |

**Example**

```bash theme={null}
curl "https://axiomlocus.io/api/zoning?metro=denver&category=commercial"
```

**Response**

```json theme={null}
{ "metro": "denver", "total": 8, "districts": [{ "district_code": "C-MX-5", "max_height_ft": 65, "max_far": 3.0 }] }
```

| Field                       | Type   | Description                     |
| --------------------------- | ------ | ------------------------------- |
| `districts[].district_code` | string | Zoning designation code         |
| `districts[].max_height_ft` | number | Maximum building height in feet |
| `districts[].max_far`       | number | Floor area ratio                |

## `GET` `/api/port-risk`

Surface upstream port disruption affecting a Locus metro's economic catchment. Twenty-two metros are mapped to their primary and secondary ports by trucking corridor (e.g. `phoenix` → Long Beach via I-10, `atlanta` → Savannah via I-16). When an inbound port shows elevated wait times in [Overwatch](/overwatch/api/disruptions), the row appears here with a `risk_level` of `watch`, `elevated`, or `severe`, derived from p90 wait, median wait, and trend direction. Returns an empty `risks` array when no upstream port is currently disrupted — the [PortRiskBadge](/locus/dashboard) on cell-detail panels uses that signal to hide itself.

Use this to flag CRE locations whose tenants depend on container imports (industrial, logistics, big-box retail) before a soft port closure shows up in occupancy or rent comps.

**Query parameters**

| Name    | Type   | Required | Default | Description                                        |
| ------- | ------ | -------- | ------- | -------------------------------------------------- |
| `metro` | string | ✓        |         | Metro slug (e.g. `houston`, `atlanta`, `chicago`). |

**Example**

```bash theme={null}
curl "https://axiomlocus.io/api/port-risk?metro=atlanta"
```

**Response**

```json theme={null}
{
  "metro": "atlanta",
  "risks": [
    {
      "port_slug": "savannah",
      "port_name": "Port of Savannah",
      "tier": "primary",
      "trucking_km": 400,
      "median_wait_hours": 18,
      "p90_wait_hours": 84,
      "vessel_count": 23,
      "trend_direction": "worsening",
      "trend_consecutive_periods": 3,
      "risk_level": "severe",
      "measured_at": "2026-04-29T00:00:00Z"
    }
  ]
}
```

| Field                               | Type   | Description                                                                                                                     |
| ----------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------- |
| `metro`                             | string | Metro slug echoed from the request.                                                                                             |
| `risks`                             | array  | Active port-risk rows, ordered by `risk_level` then `p90_wait_hours` (highest first). Empty when no upstream port is disrupted. |
| `risks[].port_slug`                 | string | Stable port identifier (e.g. `savannah`, `long-beach`, `ny-nj`).                                                                |
| `risks[].port_name`                 | string | Human-readable port name.                                                                                                       |
| `risks[].tier`                      | string | `primary` or `secondary` — the port's role in the metro's catchment.                                                            |
| `risks[].trucking_km`               | number | Driving distance from port to metro centroid (km).                                                                              |
| `risks[].median_wait_hours`         | number | Median vessel wait time at the port over the measurement window.                                                                |
| `risks[].p90_wait_hours`            | number | 90th-percentile wait — the figure that drives most risk-level transitions.                                                      |
| `risks[].vessel_count`              | number | Vessels currently queued or anchored upstream.                                                                                  |
| `risks[].trend_direction`           | string | `worsening`, `stable`, or `improving`.                                                                                          |
| `risks[].trend_consecutive_periods` | number | Periods the trend has held — used to confirm sustained disruption vs. a single-day spike.                                       |
| `risks[].risk_level`                | string | `watch`, `elevated`, or `severe`.                                                                                               |
| `risks[].measured_at`               | string | ISO 8601 timestamp of the underlying Overwatch measurement.                                                                     |

## `GET` `/api/export`

<Note>Requires Bearer token.</Note>

Export scored location data as CSV or JSON. Requires Pro or Team plan (`bulk_export` entitlement).

**Query parameters**

| Name     | Type    | Required | Default | Description                                                                                                                                |
| -------- | ------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `metro`  | string  |          |         | Metro slug filter (e.g. `sf`, `nyc`).                                                                                                      |
| `format` | string  |          | `csv`   | `csv` or `json`.                                                                                                                           |
| `saved`  | boolean |          | `false` | When `true`, restricts the export to the caller's saved cells (the cells in their `monitored_locations` portfolio). Composes with `metro`. |
| `limit`  | int     |          | `1000`  | Max rows (5000 max).                                                                                                                       |

**Portfolio export (`saved=true`)**

Set `saved=true` to export only the cells the authenticated user has saved into their portfolio (their `monitored_locations`). This is the intended workflow for analysts who track a curated set of cells across metros and want a CSV/JSON snapshot of just those rows rather than a full-metro or platform-wide pull.

* The filter is applied as an `IN` over the caller's saved `h3_index` list, so the result respects ordering by `composite` (descending) and the `limit` cap.
* It composes with `metro` — combining `saved=true&metro=sf` returns only saved cells inside SF.
* If the user has no saved cells, the endpoint short-circuits to an empty payload (`200 OK`) without scanning `cell_scores`. CSV responses return an empty body; JSON responses return `{ "total": 0, "data": [] }`.
* The download filename is tagged `saved` (e.g. `axiom-locus-saved-2026-04-29.csv`) instead of the metro slug or `all`, so portfolio exports are easy to identify on disk.

**Examples**

Export all SF cells as CSV:

```bash theme={null}
curl "https://axiomlocus.io/api/export?metro=sf&format=csv" -H "X-API-Key: al_xxx"
```

Export the caller's saved cells (portfolio) as CSV:

```bash theme={null}
curl "https://axiomlocus.io/api/export?saved=true&format=csv" -H "X-API-Key: al_xxx"
```

Export the caller's saved cells inside one metro as JSON:

```bash theme={null}
curl "https://axiomlocus.io/api/export?saved=true&metro=sf&format=json" -H "X-API-Key: al_xxx"
```

**Response**

```csv theme={null}
h3_index,metro_slug,location_name,composite,...
88283082e7fffff,sf,Mission District,74,...
```

## `GET` `/api/bids`

Search federal and government procurement opportunities sourced from SAM.gov. Filter by state, keyword, or NAICS code to find relevant contracts.

**Query parameters**

| Name      | Type   | Required | Default | Description                                                    |
| --------- | ------ | -------- | ------- | -------------------------------------------------------------- |
| `state`   | string |          |         | Two-letter state code (e.g. `TX`, `CA`).                       |
| `keyword` | string |          |         | Full-text search across title and description.                 |
| `naics`   | string |          |         | NAICS code filter (e.g. `236220` for commercial construction). |
| `type`    | string |          |         | Bid type (partial match).                                      |
| `active`  | string |          | `true`  | Set to `false` to include expired bids.                        |
| `limit`   | int    |          | `50`    | Max results (max 200).                                         |

**Example**

```bash theme={null}
curl "https://axiomlocus.io/api/bids?state=TX&keyword=construction&limit=10"
```

**Response**

```json theme={null}
{
  "state": "TX",
  "keyword": "construction",
  "count": 10,
  "bids": [
    {
      "notice_id": "SAM-2026-001234",
      "title": "Renovation of Federal Building, Houston TX",
      "type": "Solicitation",
      "agency": "General Services Administration",
      "posted_date": "2026-04-10",
      "response_deadline": "2026-05-15",
      "description": "Full interior renovation of the federal courthouse...",
      "naics_code": "236220",
      "place_of_performance_city": "Houston",
      "place_of_performance_state": "TX",
      "estimated_value": 4500000,
      "url": "https://sam.gov/opp/abc123",
      "set_aside_type": "Small Business"
    }
  ]
}
```

| Field                      | Type   | Description                                                       |
| -------------------------- | ------ | ----------------------------------------------------------------- |
| `count`                    | int    | Number of matching bids returned.                                 |
| `bids[].notice_id`         | string | SAM.gov notice identifier.                                        |
| `bids[].response_deadline` | string | Deadline for bid submissions.                                     |
| `bids[].naics_code`        | string | NAICS industry classification code.                               |
| `bids[].estimated_value`   | number | Estimated contract value in USD.                                  |
| `bids[].set_aside_type`    | string | Set-aside designation (e.g. `Small Business`, `8(a)`, `HUBZone`). |
