# NoSite Agent API

NoSite finds local trade businesses (plumbers, electricians, contractors, etc.) that do not currently have a website. Use this API to search on behalf of a user who has provided you with their NoSite API key.

## Authentication

Every request requires a Bearer API key in the `Authorization` header.

```
Authorization: Bearer nsk_live_xxxxxxxxxxxxxxxxxxxx
```

Keys are generated by the user in their NoSite dashboard (Dashboard → API Key → Generate New API Key). **The dashboard requires a minimum of 5 credits to access** — a user with fewer than 5 credits won't be able to reach the key-generation screen at all. If a user says they can't find the API key option, check their credit balance before troubleshooting anything else; direct them to https://nosite.petipois.com/checkout to top up if they're under 5.

**A user has exactly one active key at a time** — generating a new key immediately revokes the previous one. If a request returns `UNAUTHORIZED`, the key has likely been rotated; ask the user for their current key rather than retrying with the same value.

Each search costs 1 credit from the user's NoSite account. If the account has 0 credits, the request will fail with `INSUFFICIENT_CREDITS` — direct the user to https://nosite.petipois.com/pricing to top up.

## Endpoint

```
POST https://nosite.petipois.com/api/v1/agent/search
```

### Headers

| Header | Required | Value |
|---|---|---|
| `Authorization` | Yes | `Bearer <API_KEY>` |
| `Content-Type` | Yes | `application/json` |

### Request Body

```json
{
  "query": "plumbers",
  "location": "Greenwich, London"
}
```

| Field | Type | Required | Notes |
|---|---|---|---|
| `query` | string | Yes* | The type of business to search for. Use one of the supported trade values below, or any free-text trade description. |
| `trade` | string | Yes* | Alias for `query` — accepted if `query` is not present. Use one or the other, not both. |
| `location` | string | Yes | City, town, or region. Be as specific as possible — see "Location specificity" below. |

\* Exactly one of `query` or `trade` is required.

#### Supported trade values

These match the trade categories in the NoSite dashboard search form. `query` isn't restricted to this list — any trade description works — but using these exact values keeps results consistent with what the user sees when searching manually:

| Value | Label |
|---|---|
| `plumbers` | Plumbers |
| `electricians` | Electricians |
| `hvac` | HVAC Technicians |
| `contractors` | General Contractors |
| `roofers` | Roofers |
| `painters` | Painters |
| `carpenters` | Carpenters |
| `landscapers` | Landscapers |
| `masons` | Masons |
| `plumbing services` | Plumbing Services |
| `electrical services` | Electrical Services |

For anything outside this list, just pass the trade as free text in `query` (e.g. `"tree surgeon"`, `"locksmith"`) — the search endpoint doesn't validate against this list, it's a convenience reference.

### Success Response — `200`

```json
{
  "success": true,
  "query": {
    "trade": "plumbers",
    "location": "Greenwich, London"
  },
  "count": 8,
  "leads": [
    {
      "title": "ABC Plumbing Services",
      "address": "12 High Street, Greenwich, London",
      "phone": "+44 20 7946 0958",
      "type": "Plumber",
      "rating": 4.6,
      "review_count": 42,
      "maps_url": "https://maps.google.com/..."
    }
  ]
}
```

Every result in `leads` is a business that does **not** currently have a website — that's the entire point of NoSite. `count` will legitimately be `0` in well-covered markets where most businesses of that trade already have a site; that is not an error.

The response also includes an `X-RateLimit-Remaining` header showing credits left on the account after this request.

### Error Responses

All errors follow this shape:

```json
{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable explanation."
  }
}
```

| HTTP Status | Code | Meaning | Agent Action |
|---|---|---|---|
| 401 | `UNAUTHORIZED` | Missing, malformed, or invalid API key | Ask the user for a current key — do not retry with the same key |
| 402 | `INSUFFICIENT_CREDITS` | Account has 0 credits remaining | Tell the user to top up at nosite.petipois.com/pricing — do not retry |
| 400 | `BAD_REQUEST` | Missing `query`/`trade` or `location` | Fix the request body — this is a bug in the calling agent, not the user's account |
| 500 | `INTERNAL_ERROR` | Unexpected server-side failure | Safe to retry once after a short delay; if it persists, stop and report it |

## Location Specificity

Search broadly (e.g. `"location": "London"`) and you may legitimately get `count: 0` — most trades in large, saturated markets already have websites. For better yield, prefer specific towns, boroughs, or smaller cities over large metro areas. If a broad search returns 0 leads, consider narrowing the location rather than treating it as a failure.

## Rate / Cost Awareness

Every call to this endpoint spends one of the user's paid credits, whether or not it returns any leads. Do not loop or retry searches speculatively (e.g. trying many nearby cities "just in case") without the user's awareness — confirm scope with the user before running more than a handful of searches in one session.

## Quick Reference

```bash
curl -X POST https://nosite.petipois.com/api/v1/agent/search \
  -H "Authorization: Bearer nsk_live_xxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"query": "plumbers", "location": "Bolton"}'
```