Authentication

The Grand Public API uses the OAuth 2.0 client credentials grant. You exchange a client_id and client_secret for a short-lived bearer token, then send that token on every other request.

This flow is designed for server-to-server use. Your client_secret must never be embedded in a browser, mobile app, or any client your end users can inspect.

1. Get your credentials

Your client_id and client_secret are issued by Grand. Contact your account manager or email support@heygrand.com to obtain them.

Treat your client_secret like a password. Store it in a secrets manager or environment variable — never commit it to source control. If a secret is exposed, contact us to have it rotated.

2. Request a token

Send a POST to the token endpoint with your credentials as a JSON body:

curl -X POST https://public-api.heygrand.com/v1/oauth/token \
  -H 'Content-Type: application/json' \
  -d '{
    "grant_type": "client_credentials",
    "client_id": "<your-client-id>",
    "client_secret": "<your-client-secret>"
  }'
Field Required Value
grant_type Yes Must be client_credentials.
client_id Yes Your client ID.
client_secret Yes Your client secret.

3. Read the token response

{
  "access_token": "eyJraWQiOiJ...",
  "token_type": "Bearer",
  "expires_in": 3600
}
Field Description
access_token The bearer token to send on subsequent requests.
token_type Always Bearer.
expires_in Token lifetime in seconds (3600 = one hour).

No refresh token is issued. When a token expires you simply request a new one using the same client credentials.

4. Call the API with your token

Send the token in the Authorization header on every request to /v1/search and /v1/profile:

curl -H "Authorization: Bearer <access_token>" \
  "https://public-api.heygrand.com/v1/profile/12345678?jurisdiction=GB"

Token lifecycle

Tokens last one hour. Don't request a fresh token on every API call — that's slower and counts against your rate limit. Instead, cache the token and reuse it until it's close to expiring, then request a new one.

A robust pattern:

  1. Request a token and record when it expires (now + expires_in).
  2. Reuse the cached token for all requests.
  3. Refresh it proactively ~60 seconds before expiry, or reactively if you receive a 401.
import os, time, requests

_token = None
_expires_at = 0

def get_token():
    global _token, _expires_at
    # Refresh ~60s before expiry.
    if _token is None or time.time() > _expires_at - 60:
        resp = requests.post(
            "https://public-api.heygrand.com/v1/oauth/token",
            json={
                "grant_type": "client_credentials",
                "client_id": os.environ["GRAND_CLIENT_ID"],
                "client_secret": os.environ["GRAND_CLIENT_SECRET"],
            },
        )
        resp.raise_for_status()
        body = resp.json()
        _token = body["access_token"]
        _expires_at = time.time() + body["expires_in"]
    return _token

Authentication errors

Status When it happens How to fix
401 on /v1/oauth/token The client_id or client_secret is wrong. Check your credentials. Contact support if they should be valid.
401 on /v1/search or /v1/profile The Authorization header is missing, malformed, or the token has expired. Ensure the header is Authorization: Bearer <token> and request a fresh token if it has expired.

See Errors & status codes for the full list and the error response shape.

Grand Public API