# Search around an address

> Takes an address (or lat/lng, resolved the same way) and returns everything nearby, grouped the way a person reads a neighborhood rather than as one flat relevance list.

Source: https://api.tailend.dev/docs/api/search-around-address

```http
GET https://api.tailend.dev/v1/{metro}/around
X-API-Key: $TAILEND_API_KEY
```

Takes an **address** (or `lat`/`lng`, resolved the same way) and
returns everything nearby, grouped the way a person reads a
neighborhood rather than as one flat relevance list. `/nearby` is the
point-only sibling: a plain distance-ordered list, no address
resolution, no grouping — use it when the caller already has
coordinates.

One request returns:

* `location` — the resolved address, so you can see which reading of
  an ambiguous address was used;
* `activity` — a header: score, percentile, one sentence, components;
* `results` — five ranked groups, each item carrying `distance_m` and
  `walk_minutes`;
* `facets` — counts by type, age band, free/paid and distance ring
  over the whole circle;
* `coverage` — how many nearby rows are excluded for lacking
  coordinates.

Check `coverage` before treating an empty group as a signal: an empty
group with `coverage.hidden_nearby > 0` means those rows are not
visible to this endpoint, not that the area has nothing happening.

The groups: `happening_soon` (dated events in the window),
`recurring_programs` (weekly/monthly series and drop-in classes),
`classes_enrolling` (term classes), `camps`, and `places` (places with
no date of their own). `happening_soon` and `recurring_programs`
overlap by design; each item's `also_in` names the other groups it
appears in.

Results are ordered for relevance to the requested point, window and
filters. Diagnostic ranking details are available only to internal keys.

`limit` is **per group**. Each group returns its own `next_cursor`
(`"<group>:<offset>"`); pass it as `cursor` to continue that one
group.

The window is `from`/`to`, local calendar dates in the metro's
timezone: `from` defaults to today, `to` to `from` + 30 days, `to` is
inclusive of that whole local day, and the span is at most 366 days.
Unlike the collection endpoints, this route never returns an instant
before now — a `from` of today (or earlier) starts at the present,
not local midnight.
`meta.window` echoes the resolved dates.

`min_confidence` uses the default shown in this operation. Collection
endpoints may use different documented defaults.

Returns 503 when this endpoint is unavailable.

**Beta — may not be enabled for all keys yet.** If location resolution
is unavailable, returns `503` (`"geocoding unavailable"`) rather than
guessing. `data.location.source` says how the location was resolved
(see `ResolvedLocation`).

Authorization: send your key in the `X-API-Key` header. Available on every plan.

## Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `metro` | string | yes | Metro slug from /v1/metros (e.g. philadelphia). |

## Query parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `lat` | number | no | Latitude of the point to score. |
| `lng` | number | no | Longitude of the point to score. |
| `address` | string | no | A street address to resolve server-side instead of sending a point. |
| `radius_m` | integer | no | Radius in metres. |
| `from` | string (date) | no | Local calendar date (metro timezone), inclusive. |
| `to` | string (date) | no | Local calendar date (metro timezone), inclusive; at most 366 days after from. |
| `ages` | array of string | no | Age bands to favour. |
| `free` | boolean | no | Events, classes, and camps only; ignored for places. |
| `types` | array of string | no | Restrict to these listing types. |
| `min_confidence` | number | no | Minimum row confidence. |
| `limit` | integer | no | Maximum items per group, not per response. |
| `cursor` | string | no | A group's next_cursor from the previous response: "<group>:<offset>". |
| `include` | array of string | no | Opt into a block the lean default response leaves out. |

## Example request

```bash
curl -sS \
  -H "X-API-Key: $TAILEND_API_KEY" \
  "https://api.tailend.dev/v1/philadelphia/around"
```

## Responses

### 200

The resolved location, the activity header, grouped results, facets and coverage.

```json
{
  "data": {
    "location": {
      "lat": 1,
      "lng": 1,
      "source": "cache"
    },
    "results": {
      "happening_soon": {
        "total": 1,
        "returned": 1,
        "items": [
          {
            "listing_id": "00000000-0000-4000-8000-000000000001",
            "type": "event",
            "name": "example",
            "distance_m": 1,
            "walk_minutes": 1,
            "score": 1
          }
        ]
      },
      "recurring_programs": {
        "total": 1,
        "returned": 1,
        "items": [
          {
            "listing_id": "00000000-0000-4000-8000-000000000001",
            "type": "event",
            "name": "example",
            "distance_m": 1,
            "walk_minutes": 1,
            "score": 1
          }
        ]
      },
      "classes_enrolling": {
        "total": 1,
        "returned": 1,
        "items": [
          {
            "listing_id": "00000000-0000-4000-8000-000000000001",
            "type": "event",
            "name": "example",
            "distance_m": 1,
            "walk_minutes": 1,
            "score": 1
          }
        ]
      },
      "camps": {
        "total": 1,
        "returned": 1,
        "items": [
          {
            "listing_id": "00000000-0000-4000-8000-000000000001",
            "type": "event",
            "name": "example",
            "distance_m": 1,
            "walk_minutes": 1,
            "score": 1
          }
        ]
      },
      "places": {
        "total": 1,
        "returned": 1,
        "items": [
          {
            "listing_id": "00000000-0000-4000-8000-000000000001",
            "type": "event",
            "name": "example",
            "distance_m": 1,
            "walk_minutes": 1,
            "score": 1
          }
        ]
      }
    },
    "facets": {
      "by_type": {},
      "by_age_band": {},
      "by_price": {
        "free": 1,
        "paid": 1
      },
      "free_share": 1,
      "by_distance_ring": [
        {
          "label": "within_400m",
          "max_m": 1,
          "count": 1
        }
      ]
    },
    "coverage": {
      "hidden_nearby": 1,
      "unplaceable_in_metro": 1,
      "reason": "example"
    }
  },
  "meta": {
    "metro": "example",
    "radius_m": 1,
    "window": {
      "from": "2026-09-24",
      "to": "2026-09-24",
      "timezone": "example"
    },
    "limit": 1
  }
}
```

### 400

Malformed or contradictory parameters (lat without lng, to before from, unknown category), or a cursor this API did not issue for this exact request — replayed from another endpoint, resource, metro or filter set, hand-crafted, or altered.

```json
{
  "error": {
    "code": "bad_request",
    "message": "radius_m: must be at least 100",
    "details": {
      "param": "radius_m"
    },
    "request_id": "req_dfbc3295a8382b05"
  }
}
```

### 401

Missing, unknown or revoked X-API-Key.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "missing X-API-Key header",
    "request_id": "req_90e63908273f6bff"
  }
}
```

### 403

Two different reasons share this status.

```json
{
  "error": {
    "code": "forbidden",
    "message": "key is not scoped to metro 'boston'",
    "details": {
      "metros": [
        "philadelphia"
      ]
    },
    "request_id": "req_9e7a750acef48ab2"
  }
}
```

### 404

No published, active resource with that id (or unknown metro).

```json
{
  "error": {
    "code": "not_found",
    "message": "unknown metro 'boston'",
    "request_id": "req_7431b51e10ec2b77"
  }
}
```

### 405

The path exists; the method does not.

```json
{
  "error": {
    "code": "method_not_allowed",
    "message": "POST is not allowed on this path; this API is read-only",
    "details": {
      "allow": [
        "GET",
        "HEAD",
        "OPTIONS"
      ]
    },
    "request_id": "req_01J9Z3"
  }
}
```

### 429

A limit was reached.

```json
{
  "error": {
    "code": "rate_limited",
    "message": "3 requests per minute exceeded",
    "request_id": "req_06bfd3c2d5e8aa9f"
  }
}
```

### 500

A bug on our side.

```json
{
  "error": {
    "code": "internal",
    "message": "internal error",
    "request_id": "req_01J9Z3"
  }
}
```

### 501

This endpoint or feature is not available and will not become available without a configuration change — for ?address=, this means no geocoder is configured.

```json
{
  "error": {
    "code": "not_implemented",
    "message": "this endpoint is not enabled on this deployment; it is not a fault and retrying will not help",
    "request_id": "req_01J9Z3"
  }
}
```

### 503

A transient outage: a dependency could not be reached or did not answer in time.

```json
{
  "error": {
    "code": "unavailable",
    "message": "the service is temporarily unable to serve this request; retry after the interval in Retry-After",
    "request_id": "req_01J9Z3"
  }
}
```
