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:
- Request a token and record when it expires (
now + expires_in). - Reuse the cached token for all requests.
- 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.