Developers

FindFetcher API and MCP server

Create and manage Australian price, ticket, experience and car-listing watches for a FindFetcher user, and read the matches. A REST API and an MCP server, both behind OAuth 2.1. Prices are in AUD dollars.

API + MCP live in beta

At a glance

  • REST base URL https://findfetcher.com.au/api/v1, described in OpenAPI 3.1.
  • MCP server https://findfetcher.com.au/api/mcp (Streamable HTTP) with tools create_price_watch, list_price_watches, get_matches, edit_price_watch, cancel_price_watch, resume_price_watch, switch_price_watch_cadence, create_gift_finder, edit_gift_ideas.
  • OAuth 2.1 authorization code with PKCE (S256), dynamic client registration, rotating refresh tokens.

Quickstart

  1. Fastest: connect the MCP server

    Add https://findfetcher.com.au/api/mcp as a custom connector (remote MCP, Streamable HTTP) in Claude, ChatGPT, Cursor, VS Code or any MCP client. The client registers itself and opens FindFetcher so the human can log in and approve. Nothing else to set up.

  2. 1. Register your client (RFC 7591)

    Public clients use PKCE and need no secret.

    curl -X POST https://findfetcher.com.au/api/oauth/register \
      -H "Content-Type: application/json" \
      -d '{"client_name":"My assistant","redirect_uris":["https://example.com/callback"],"token_endpoint_auth_method":"none"}'
  3. 2. Send the human to approve

    Open this URL in their browser with a PKCE S256 challenge. They log in to FindFetcher (or start a free trial) and approve the scopes.

    https://findfetcher.com.au/api/oauth/authorize?response_type=code&client_id=$CLIENT_ID&redirect_uri=https%3A%2F%2Fexample.com%2Fcallback&scope=agent%3Aread%3Afetches%20agent%3Awrite%3Afetches%20agent%3Aread%3Amatches&code_challenge=$CODE_CHALLENGE&code_challenge_method=S256&state=$STATE
  4. 3. Exchange the code for tokens

    Access tokens last 1 hour. Refresh tokens rotate on every use.

    curl -X POST https://findfetcher.com.au/api/oauth/token \
      -d grant_type=authorization_code \
      -d code=$CODE \
      -d redirect_uri=https://example.com/callback \
      -d client_id=$CLIENT_ID \
      -d code_verifier=$CODE_VERIFIER
  5. 4. Call the API

    List the human's price watches.

    curl https://findfetcher.com.au/api/v1/price-watches \
      -H "Authorization: Bearer $ACCESS_TOKEN"

Authentication and scopes

Send Authorization: Bearer <access token> on every call. Ask only for the scopes you need; the human sees them on the consent screen.

  • agent:read:fetches
  • agent:write:fetches
  • agent:read:matches
  • agent:read:account
  • agent:read:webhooks
  • agent:write:webhooks

There are no API keys: every call acts for one human who approved your client through OAuth, so access is always theirs to give and take back. There is no separate sandbox. To test end to end, start a 14-day free trial with your own account and connect to it.

API reference

List price watches

GET /api/v1/price-watches scope agent:read:fetches, operationId listPriceWatches

Every price watch on the human's account, newest first, with prices in AUD dollars.

curl https://findfetcher.com.au/api/v1/price-watches \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Create a price watch

POST /api/v1/price-watches scope agent:write:fetches, operationId createPriceWatch

Start watching for something in Australia (a product, tickets, an experience or a car). Uses one of the human's plan slots. Put the budget in constraints.maxPrice (AUD dollars). The first check runs within minutes.

curl -X POST https://findfetcher.com.au/api/v1/price-watches \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"queryText":"Sony WH-1000XM5","category":"retail","constraints":{"maxPrice":400}}'

Edit, pause or resume a price watch

PATCH /api/v1/price-watches/{id} scope agent:write:fetches, operationId updatePriceWatch

Change the name, query or constraints, or set status to "paused" or "active".

curl -X PATCH https://findfetcher.com.au/api/v1/price-watches/3f1c2b7e-0000-4000-8000-000000000000 \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"status":"paused"}'

Cancel a price watch

DELETE /api/v1/price-watches/{id} scope agent:write:fetches, operationId cancelPriceWatch

Stops the watch and frees its plan slot. Matches already found are kept.

curl -X DELETE https://findfetcher.com.au/api/v1/price-watches/3f1c2b7e-0000-4000-8000-000000000000 \
  -H "Authorization: Bearer $ACCESS_TOKEN"

List matches for a price watch

GET /api/v1/price-watches/{id}/matches scope agent:read:matches, operationId listPriceWatchMatches

The listings FindFetcher found for this watch, with price, store and link.

curl https://findfetcher.com.au/api/v1/price-watches/3f1c2b7e-0000-4000-8000-000000000000/matches \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Switch a price watch between daily and premium checks

POST /api/v1/price-watches/{id}/cadence scope agent:write:fetches, operationId setPriceWatchCadence

"hourly" moves the watch to the Pro premium slot (checked every few hours); "daily" moves it back.

curl -X POST https://findfetcher.com.au/api/v1/price-watches/3f1c2b7e-0000-4000-8000-000000000000/cadence \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"cadence":"daily"}'

Create a gift finder

POST /api/v1/gift-finders scope agent:write:fetches, operationId createGiftFinder

Describe who a gift is for, what they love and the budget; FindFetcher suggests gift ideas and watches Australian stores for them. Returns 200 with GiftFinderMissing when the budget or occasion date is needed: ask the human, then call again.

curl -X POST https://findfetcher.com.au/api/v1/gift-finders \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"brief":"My dad, 60s, loves gardening and good coffee, for his birthday","budget_aud":80,"occasion_date":"2026-12-20"}'

Edit a gift finder's ideas

PATCH /api/v1/gift-finders/{id} scope agent:write:fetches, operationId editGiftFinderIdeas

Add, remove, pin or unpin gift ideas, or refresh the list.

curl -X PATCH https://findfetcher.com.au/api/v1/gift-finders/3f1c2b7e-0000-4000-8000-000000000000 \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"op":"add_idea","label":"pour-over coffee set"}'

List webhook endpoints

GET /api/v1/webhook-endpoints scope agent:read:webhooks, operationId listWebhookEndpoints

The endpoints this assistant registered for match.created events. Signing secrets are never listed.

curl https://findfetcher.com.au/api/v1/webhook-endpoints \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Register a webhook endpoint

POST /api/v1/webhook-endpoints scope agent:write:webhooks, operationId createWebhookEndpoint

Receive HMAC-signed match.created events at a public HTTPS URL. The signing_secret is returned once, here. Up to 3 per human.

curl -X POST https://findfetcher.com.au/api/v1/webhook-endpoints \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/findfetcher-webhook"}'

Enable, disable or rotate a webhook endpoint

PATCH /api/v1/webhook-endpoints/{id} scope agent:write:webhooks, operationId updateWebhookEndpoint

Set enabled, and/or rotate_secret to get a new signing secret (returned once).

curl -X PATCH https://findfetcher.com.au/api/v1/webhook-endpoints/3f1c2b7e-0000-4000-8000-000000000000 \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"rotate_secret":true}'

Delete a webhook endpoint

DELETE /api/v1/webhook-endpoints/{id} scope agent:write:webhooks, operationId deleteWebhookEndpoint

Stops deliveries to this URL.

curl -X DELETE https://findfetcher.com.au/api/v1/webhook-endpoints/3f1c2b7e-0000-4000-8000-000000000000 \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Errors

Every error is JSON with error_code, message, hint and docs_url.

missing_token (401)
No access token was sent. Send an OAuth 2.1 access token as "Authorization: Bearer <token>". Get one from /api/oauth/token after the human approves your client.
invalid_token (401)
The access token is invalid, expired or revoked. Refresh it with your refresh_token at /api/oauth/token, or restart the OAuth flow if the human disconnected your assistant.
token_revoked (401)
The human disconnected this assistant, so its tokens no longer work. Restart the OAuth flow and ask the human to approve your assistant again.
insufficient_scope (403)
The access token does not include the scope this endpoint needs. Request the scope named in the WWW-Authenticate header when you start the OAuth flow.
rate_limited (429)
Too many requests. Wait for retry_after_seconds (or the Retry-After header) before trying again.
invalid_request_body (400)
The request body is missing, is not valid JSON, or does not match the schema. Send a JSON body that matches the request schema in /openapi.json.
invalid_status_value (400)
The status value is not allowed. Use "active" or "paused".
missing_price_watch_id (400)
The price watch id is missing from the URL. Call /api/v1/price-watches/{id} with an id from GET /api/v1/price-watches.
missing_gift_finder_id (400)
The gift finder id is missing from the URL. Call /api/v1/gift-finders/{id} with the id returned when the gift finder was created.
missing_webhook_endpoint_id (400)
The webhook endpoint id is missing from the URL. Call /api/v1/webhook-endpoints/{id} with an id from GET /api/v1/webhook-endpoints.
not_found (404)
Nothing exists at this API path, or the record does not belong to this user. Check the path against /openapi.json and use ids returned by the list endpoints.
webhook_endpoint_not_found (404)
No webhook endpoint with this id belongs to this user. List endpoints with GET /api/v1/webhook-endpoints and use one of those ids.
webhook_endpoint_limit (400)
This user already has the maximum number of webhook endpoints. Delete an unused endpoint with DELETE /api/v1/webhook-endpoints/{id} first.
subscription_required (403)
The human's FindFetcher account has no active plan. Ask the human to choose a plan at plans_url. An assistant cannot subscribe or pay.
slot_limit (403)
All of the active fetch slots on this plan are in use. Pause or cancel a price watch, or ask the human to add a slot or change plan at plans_url.
fetch_limit (403)
All of the active fetch slots on this plan are in use. Pause or cancel a price watch, or ask the human to change plan at plans_url.
cadence_not_allowed (403)
This check frequency is not included in the human's plan. Use "daily", or ask the human to move to Pro at plans_url for the premium cadence.
daily_create_limit (429)
This user has created the maximum number of watches for today. Try again tomorrow (Australian time).
gift_creation_unavailable (503)
Gift Finder creation is switched off right now. Create a normal price watch instead, or try again later.
agent_api_unconfigured (503)
The agent API is not configured on this server. Try again later. Contact joey@findfetcher.com.au if it persists.
internal_error (500)
Something went wrong on our side. Retry once. Contact joey@findfetcher.com.au with the time of the request if it keeps failing.

Rate limits and cost

  • 120 requests per minute per client, and 60 per minute per human.
  • 5 watch creations per minute per human, and 30 per day.
  • Every response carries RateLimit-Policy and RateLimit headers (IETF draft-ietf-httpapi-ratelimit-headers), plus RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (seconds), for the bucket closest to running out. Slow down as RateLimit-Remaining nears 0.
  • A 429 response carries a Retry-After header and retry_after_seconds in the body.
  • No per-call charges during the beta. Watches use the human's normal plan slots.

Versioning and deprecation

  • The version is in the URL: /api/v1. The MCP server follows the same rules for its tools.
  • Within v1, changes are additive only: new endpoints, new optional request fields, new response fields and new error codes. Ignore fields you do not know.
  • A breaking change ships as a new version (/api/v2). The old version keeps working for at least 90 days after the new one launches.
  • From the day an endpoint or version is deprecated, its responses carry a Deprecation header (RFC 9745), a Sunset header with the shutdown date (RFC 8594) and a Link header with rel="deprecation" pointing at the migration notes.
  • Every change is announced on /developers and in /agents.md first. The API is in beta, but these rules already apply.

Machine-readable files