Skip to content

API Documentation

TrashAlert exposes two API surfaces: a free, unauthenticated resident lookup endpoint that backs the site's own address search, and a preview business API (/api/v1) for account-bound, pilot-approved integrations.

Resident API — Free, No API Key

Base URL: https://trashalert.io
Rate limit: 30 requests per minute per IP. This limit is enforced process-locally (an in-memory counter per running server instance) — it is not a durable, shared, or cross-instance quota, and it resets on every cold start or redeploy.
This endpoint remains free for residents looking up their own schedule. No account, API key, or billing is required.
GET/api/schedule

Look up trash pickup schedule by address or ZIP code. Free and unauthenticated — this is the same endpoint the TrashAlert site itself uses.

Parameters

address(string)* — Street address (e.g., "4567 Park Blvd"). Not required when using zip.
city(string) — City name (default: "san diego")
zip(string) — ZIP code — alternative to an address lookup

Response

{
  "found": true,
  "match": "exact" | "fuzzy" | "city_api" | "db_zone" | "hauler_api" | "zip_fallback",
  "matchedAddress": "4567 park blvd, san diego, ca 92103",
  "schedule": {
    "collection_day": "wednesday",
    "recycling_week": "A",
    "verified": true,
    "verification_count": 3,
    "neighborhood": "university-heights"
  }
}

Look up by address (curl)

curl "https://trashalert.io/api/schedule?address=4567+Park+Blvd"

Business API v1 — Preview, Approved Pilot Accounts

Status: Preview / pilot-stage. There is no self-service signup, no announced availability date for a wider release, no public pricing, and no automated key-issuance flow — every credential is manually approved. Access is granted only after TrashAlert approves an account for the pilot — contact sales to request it. TrashAlert does not publish a service-level commitment for this API.

Auth: Send Authorization: Bearer ta_test_… or ta_live_… on the schedules, coverage, and usage endpoints below — except GET /api/v1/openapi, which is public. The credential-management endpoints (/api/v1/credentials) are different: they require an authenticated portfolio-account session (the same one used to sign in to the dashboard), not an API key — a leaked API key can never use these endpoints to mint or revoke other credentials. Requests use a monthly, account-level request-unit quota (not the resident endpoint's per-IP rate limit); GET /api/v1/usage reports the current quota and does not itself consume a unit.

The full machine-readable contract is published at /api/v1/openapi (OpenAPI 3.1).
POST/api/v1/schedules/lookupscope: schedule:read

Resolve one address against TrashAlert's coverage data. Consumes one request unit.

Request Body

{
  "external_id": "unit-1",
  "address": {
    "line1": "4567 Park Blvd",
    "city": "San Diego",
    "state": "CA",
    "postal_code": "92103"
  }
}
  • Optional Idempotency-Key header (1–200 characters).
  • Response status field is one of resolved, unsupported, not_found, or ambiguous — never a fabricated schedule.

Response

{
  "request_id": "uuid",
  "replay": false,
  "data": {
    "external_id": "unit-1",
    "status": "resolved",
    "normalized_address": { "line1": "...", "city": "...", "state": "CA", "postal_code": "92103" },
    "coverage": { "status": "live", "city_slug": "san-diego", "source": "...", "detail": null },
    "schedule": { "services": [ { "service": "trash", "days": ["wednesday"], "cadence": "weekly", "cycle": null } ], "verified": true, "verification_count": 3, "source": "schedule_reports", "fetched_at": null },
    "candidates": []
  }
}
POST/api/v1/schedules/batchscope: schedule:read

Resolve up to 25 addresses in one call (the account's batch limit may be lower). Each address consumes one request unit.

Request Body

{
  "requests": [
    { "external_id": "unit-1", "address": { "line1": "4567 Park Blvd", "city": "San Diego", "state": "CA", "postal_code": "92103" } }
  ]
}
  • Idempotency-Key header is required (1–200 characters) — the same key with a different body returns 409 idempotency_conflict.

Response

{
  "request_id": "uuid",
  "replay": false,
  "data": {
    "results": [ { "external_id": "unit-1", "status": "resolved", "normalized_address": { ... }, "coverage": { ... }, "schedule": { ... }, "candidates": [] } ],
    "result_counts": { "resolved": 1, "unsupported": 0, "not_found": 0, "ambiguous": 0, "invalid": 0, "error": 0 }
  }
}
GET/api/v1/coveragescope: coverage:read

List explicit coverage status per city. A live status does not guarantee every address in that city will resolve.

Query Parameters

status(string) — Filter: live, limited, coming_soon, or pilot
state(string) — Two-letter US state abbreviation

Response

{
  "request_id": "uuid",
  "data": {
    "records": [ { "city_slug": "san-diego", "city": "San Diego", "state": "CA", "status": "live", "source": "...", "detail": null } ],
    "count": 1,
    "filters": { "status": null, "state": null },
    "qualifier": "A live city path does not guarantee that every address in the city will resolve."
  }
}
GET/api/v1/usagescope: usage:read

Read the account's current monthly quota and result counts. Does not consume a request unit, so it stays available even after the quota is exhausted.

Response

{
  "request_id": "uuid",
  "data": {
    "period_start": "2026-09-01",
    "period_end": "2026-10-01",
    "quota_limit": 5000,
    "units_used": 12,
    "units_remaining": 4988,
    "batch_limit": 25,
    "request_count": 10,
    "batch_item_count": 12,
    "result_counts": { "resolved": 9, "unsupported": 1, "not_found": 0, "ambiguous": 0, "invalid": 0, "error": 0, "rate_limited": 0 },
    "usage_endpoint_metered": false
  }
}
POST/api/v1/credentials

Creates an account-scoped API credential. Requires an authenticated portfolio-account session (dashboard login) — API keys cannot create other credentials. The plaintext secret is returned only in this response.

Request Body

{
  "name": "Pilot integration",
  "environment": "test",
  "scopes": ["schedule:read", "usage:read"]
}
  • Auth: portfolio-account session, not an API key — this endpoint manages keys, so a leaked key cannot use it to mint more.
  • environment defaults to test; scopes accepts 1 to 3 of schedule:read, coverage:read, usage:read and defaults to all three when omitted.

Response

{
  "request_id": "uuid",
  "data": {
    "credential": { "id": "uuid", "account_id": "uuid", "name": "Pilot integration", "environment": "test", "key_prefix": "ta_test_...", "scopes": ["schedule:read", "usage:read"], "status": "active", "created_at": "...", "revoked_at": null, "expires_at": null },
    "secret": "ta_test_... (shown once — save it now)"
  }
}
GET/api/v1/credentials

Lists the account's API credential metadata. Requires an authenticated portfolio-account session. Plaintext secrets and secret hashes are never included.

Query Parameters

environment(string) — Filter: test or live
  • Auth: portfolio-account session, not an API key.

Response

{
  "request_id": "uuid",
  "data": {
    "credentials": [ { "id": "uuid", "account_id": "uuid", "name": "Pilot integration", "environment": "test", "key_prefix": "ta_test_...", "scopes": ["schedule:read"], "status": "active", "created_at": "...", "revoked_at": null, "expires_at": null } ]
  }
}
DELETE/api/v1/credentials/{credentialId}

Revokes an account-owned API credential. Requires an authenticated portfolio-account session. Repeating a successful revoke is idempotent — revoking an already-revoked credential still returns 200.

  • Auth: portfolio-account session, not an API key.

Response

{
  "request_id": "uuid",
  "data": { "revoked": true, "already_revoked": false, "credential_id": "uuid" }
}
GET/api/v1/openapi

Returns the machine-readable OpenAPI 3.1 document for this preview API. This endpoint does not require an API key or a portfolio session.

Response

Returns the full OpenAPI 3.1 JSON document (see link below).