Skip to main content
Borough supports two authentication methods: API keys (simplest) and OAuth 2.1 (for MCP clients and third-party integrations).

No key (Free tier)

Search, areas, market, and photo endpoints answer requests with no Authorization header at all. These keyless requests are metered per IP at the Free tier limits. Send a key only when you need paid endpoints or higher limits. A placeholder or expired key is rejected with 401; it does not fall back to the free tier.

API Key (Bearer Token)

Include your API key in the Authorization header:
Keys use the format BOROUGH-<uuid> and are provisioned automatically when you subscribe to a plan.
The key itself is checked live against Polar.sh on every request, so revocation takes effect immediately: the next request made with a revoked or disabled key returns 401. The one delayed revocation path is OAuth 2.1 access tokens (below), which are issued after validation and stay usable until they expire, at most one hour later. Two things can lag behind a subscription change. A plan change can take up to 5 minutes to apply, so right after an upgrade a key may still get the old tier’s limits, including 403 TIER_RESTRICTED. A key that was refused, for example just before its subscription was reactivated, may keep being refused for up to 60 seconds.

OAuth 2.1 (PKCE)

Borough implements OAuth 2.1 with PKCE for secure delegated access. This is the recommended method for MCP clients and third-party apps.

Endpoints

Flow

  1. Register your client (optional for localhost apps):
    Unregistered clients may only use localhost redirect URIs.
  2. Redirect user to authorize:
    The user enters their Borough API key (or continues with Free tier) and is redirected back with an authorization code. resource is optional. If you send it, it must be https://borough.qwady.app or https://borough.qwady.app/mcp (a trailing slash is fine); anything else returns 400 invalid_target.
  3. Exchange code for tokens:
  4. Use the access token:

Token lifetimes

Use the refresh token to get new access tokens without re-authorization:
Refresh tokens rotate. Each successful refresh returns a new refresh_token that must replace the one you stored, and the token you presented is consumed. Presenting a consumed token again within 60 seconds returns the same response, so a retried request is safe. After 60 seconds it counts as reuse: every refresh token in that grant is revoked (access tokens already issued last until they expire) and the call returns invalid_grant, so the user must authorize again. The refresh flow re-validates your API key, so tier changes and revocations take effect on the next refresh. If Borough cannot reach its billing provider to re-validate, the refresh returns 503 with server_error and does not consume your refresh token; retry with the same token.

Free-tier access

Search endpoints (/v1/search/rentals, /v1/search/sales), areas (/v1/areas), market endpoints, and photo delivery (/v1/photos/{key}) work without authentication and use Borough’s Free tier limits. The public /v1/health endpoint is also available without authentication, but it is outside Borough’s metered quota and rate-limit middleware. Unauthenticated metered requests are subject to:
  • 10 requests/minute rate limit (IP-based)
  • 100 requests/month quota
  • Maximum 10 results per page
Public Borough pages live on borough.qwady.app, while service integrations continue to use /v1/*, /mcp, /.well-known/*, /oauth/*, and /v1/webhooks/*. Public docs live separately at qwady.wiki/borough.

Error responses

A request with no key is treated as Free tier, so a keyless call to a paid /v1 endpoint (property, building, listing streams, or creating a watcher or webhook) returns 403 TIER_RESTRICTED, not 401. On 401 responses from authentication (a missing key on /mcp, or an invalid or expired key), the WWW-Authenticate header includes a link to the protected-resource metadata for automatic OAuth discovery.

Security best practices

  • Store keys in environment variables, never in client-side code
  • Rotate keys by creating a new subscription if compromised
  • Use the minimum tier needed for your use case
  • For OAuth, always use PKCE with S256 code challenges
  • Register your client to restrict allowed redirect URIs