# Graham & Lane Florists Public Catalog API

> Free read-only flower catalog and studio information for Edmonton, Alberta. No account, signup, API key, OAuth, subscription or payment is required. Start immediately with a GET request.

## Quickstart

The [API index](https://grahamandlane.com/api) lists the public endpoints. [Access policy](https://grahamandlane.com/auth.md): authentication is none. Send the requests below as written, without cookies or an Authorization header. A request-limit response never means that you need to log in.

```bash
curl 'https://grahamandlane.com/api/v1/arrangements.json?q=yellow&limit=10&offset=0'
curl 'https://grahamandlane.com/api/v1/studio.json'
```

[OpenAPI 3.1 specification](https://grahamandlane.com/openapi.json) describes every operation and response schema. Import it into an OpenAPI client or a function-calling agent. Both operations are read-only and have flat inputs. [Agent guidance](https://grahamandlane.com/agents.md), [discovery manifest](https://grahamandlane.com/.well-known/ard.json) and [current arrangement prices](https://grahamandlane.com/pricing) are also public.

## JavaScript and Python SDKs and CLI

Download the dependency-free source clients directly from this site. These are working JavaScript and Python SDKs, not npm or PyPI registry packages. Both support studio lookup, arrangement search, limits and the returned opaque next-page cursor. They send only anonymous GET requests, use a 15-second timeout and expose HTTP error status/code and Retry-After. They do not retry automatically, write customer data or place orders.

- [JavaScript SDK source](https://grahamandlane.com/sdk/catalog-client.mjs) and [Node CLI source](https://grahamandlane.com/sdk/catalog-cli.mjs), for Node 22 or 24.
- [Python SDK and CLI source](https://grahamandlane.com/sdk/catalog_client.py), for Python 3. No pip install required.

Save the two JavaScript files in the same directory, then run:

```bash
curl -fSLO https://grahamandlane.com/sdk/catalog-client.mjs
curl -fSLO https://grahamandlane.com/sdk/catalog-cli.mjs
node catalog-cli.mjs --help
node catalog-cli.mjs find yellow
node catalog-cli.mjs studio
```

Python quickstart:

```bash
curl -fSLO https://grahamandlane.com/sdk/catalog_client.py
python3 catalog_client.py --help
python3 catalog_client.py find yellow
python3 catalog_client.py studio
```

Import `findArrangements` / `getStudioInformation` from the JavaScript module, or `find_arrangements` / `get_studio_information` from the Python module. Pass the first response's pagination.nextCursor as cursor with the same search phrase and limit to read the next page. Stop when nextCursor is null. These clients are specific to Graham & Lane's v1 public API; copy the pattern and replace the identity for another business.

## Find arrangements

`GET /api/v1/arrangements.json`

Optional `q` searches published arrangement names, colours and occasions, case-insensitively. Supply one search phrase of at most 200 characters. Omit q to return all published arrangements. Optional `limit` (integer 1 to 100, default 100) and `offset` (non-negative integer, default 0) control the first page after filtering. Subsequent pages use an opaque `cursor` from the response; do not construct or decode it. Do not combine cursor and offset, and keep q the same. Results are ordered by product handle. The response includes `pagination.limit`, `offset`, `total`, `hasMore`, `nextCursor` and `next`. Follow the absolute `next` URL or the HTTP `Link: <URL>; rel="next"` header until `hasMore` is false. An offset beyond the matching catalog returns an empty page. Repeated page requests can reflect catalog updates; this is not a frozen inventory snapshot.

Each arrangement includes name, handle, product URL, image when available, fromPrice, colours, occasions and sizes. Prices are decimal strings with currencyCode CAD. Sizes include the published availableForSale value. Confirm stock with the florist before promising an order. No customer, recipient, card message, account or cart information is exposed.

An empty arrangements array is a successful search with no matches. Example of a result entry:

```json
{
  "name": "Peachy Keen",
  "handle": "peachy-keen",
  "url": "https://grahamandlane.com/products/peachy-keen",
  "fromPrice": {"amount": "89.95", "currencyCode": "CAD"},
  "colours": ["Yellow"],
  "occasions": ["Birthday"],
  "sizes": [{"name": "Standard", "price": {"amount": "89.95", "currencyCode": "CAD"}, "availableForSale": true}]
}
```

This is an illustrative entry, not a stock or price guarantee. Read the endpoint for current values.

## Studio information

`GET /api/v1/studio.json`

No parameters. Returns public name, description, website, telephone, email, street address, hours, deliveryAreas, ordering instructions and pickup preparation. [Contact the studio](https://grahamandlane.com/pages/contact).

## Versioning and compatibility

The canonical endpoints use `/api/v1/`. Both accept optional `API-Version: 1`; another header value returns 400 `unsupported_version`. Successful responses include `API-Version: 1`. v1 changes will remain backward compatible; breaking changes require a new URL version. Any future retirement will be announced here with a migration guide and deprecation/sunset response headers before removal. No retirement is currently scheduled.

The original `/api/arrangements.json` and `/api/studio.json` endpoints remain supported. The original arrangements endpoint returns the full matching catalog without pagination, preserving existing clients. Use v1 for new paginated integrations. All operations are synchronous public reads. Repeated GET/HEAD requests are idempotent; there are no mutation jobs or Idempotency-Key requirements.

## Public API request limits

The public REST endpoints and /api discovery index share a best-effort limit of 60 GET/HEAD requests per minute per hashed Oxygen buyer IP within each worker instance. If Oxygen has no buyer IP metadata, unidentified clients share one fallback bucket. Bucket state is ephemeral, bounded and not a global or durable quota; multiple worker instances and restarts can have independent counters. No raw IP is stored in these buckets or forwarded to analytics.

Responses advertise RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (seconds until reset). These are the public headers verified through the Oxygen deployment. When exhausted, the endpoint returns typed 429 JSON and Retry-After seconds. Respect that delay. Website shopping pages and carts are not subject to this catalog-API limiter. MCP remains bounded by its separate request/argument validation, not this REST quota.

## Errors and retries

Responses use JSON and HTTP status codes. Invalid or duplicate query parameters and invalid/search-mismatched cursors return 400. Unsupported API paths return 404. These read-only endpoints reject write methods with 405 and `Allow: GET, HEAD`. A temporarily unavailable catalog returns 503 with `Retry-After: 60`. Wait at least 60 seconds before retrying a 503. Correct a 400 request instead of retrying it. Network failures can be retried with exponential backoff; avoid rapid repeat requests.

```json
{"error":{"code":"invalid_query","message":"Use one optional q search parameter.","documentation":"https://grahamandlane.com/docs"}}
```

The API has no usage fee or separately provisioned credentials. There is no separate sandbox: all documented operations only read public information. No write or payment testing is available here.

## Prices and ordering

Prices are Canadian dollars and exclude delivery and applicable tax. Card messages over 20 words require a larger message card costing CAD 1.50. Custom wedding, event and corporate flowers require a conversation and quote.

Online purchasing is temporarily unavailable. Call [(780) 429-1637](tel:+17804291637) to order. An arrangement in a website cart is not a confirmed purchase. This API cannot place an order, reserve flowers, accept money, send an enquiry or make a booking. Agents must ask the customer before initiating a call or sharing personal details.

## Browser tools

Browsers supporting WebMCP expose `find_arrangements` (optional query string) and `get_studio_information` (no inputs). Both are read-only. Ordinary browsers can use the same HTTP endpoints without WebMCP. No extension or experimental browser feature is required for API access.

## MCP connection

Connect an MCP client to `https://grahamandlane.com/mcp` using Streamable HTTP. This is a free anonymous public server with no API key or account required. It supports initialization, ping, tools/list and tools/call. Tools are find_arrangements (optional query) and get_studio_information (no inputs).

The server is stateless, returns JSON responses and does not open an SSE stream or assign sessions. Send Content-Type application/json and Accept application/json, text/event-stream. Negotiate version 2025-11-25, 2025-06-18 or 2025-03-26, then include MCP-Protocol-Version on subsequent requests. Requests are limited to 16 KiB. Browser requests must come from the storefront origin; external server-side clients can omit Origin. There are no write tools, customer data, payment operations or OAuth scopes.

```bash
curl https://grahamandlane.com/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"example","version":"1.0.0"}}}'
```
