Search then fetch a profile
What you'll build: a small, production-minded routine that takes a company name and resolves it to a full company profile — the most common way to use the Grand Public API.
The flow is three steps, where each call feeds the next:
- Authenticate —
POST /v1/oauth/token→ an access token - Search —
GET /v1/searchwith that token → acompanyNumber - Fetch —
GET /v1/profile/{companyNumber}→ the full profile
Prerequisites
- A
client_idandclient_secret(see Authentication). - The jurisdiction you're searching —
GBorIE(see Jurisdictions).
Exchange your credentials for a token and reuse it across the next two calls. Tokens last an hour, so cache it rather than re-authenticating each time.
TOKEN=$(curl -s -X POST https://public-api.heygrand.com/v1/oauth/token \
-H 'Content-Type: application/json' \
-d "{\"grant_type\":\"client_credentials\",\"client_id\":\"$GRAND_CLIENT_ID\",\"client_secret\":\"$GRAND_CLIENT_SECRET\"}" \
| jq -r '.access_token')
Search by name within a jurisdiction. Use pageSize to control how many candidates you
get back (1–50).
curl -s -H "Authorization: Bearer $TOKEN" \
"https://public-api.heygrand.com/v1/search?q=Acme%20Trading&jurisdiction=GB&pageSize=10"
{
"results": [
{
"name": "Acme Trading Ltd",
"companyNumber": "12345678",
"jurisdiction": "GB",
"status": "Active",
"address": "123 Example Street, London, EC1A 1BB"
},
{
"name": "Acme Trading (Holdings) Ltd",
"companyNumber": "87654321",
"jurisdiction": "GB",
"status": "Dissolved",
"address": "456 Sample Road, Manchester, M1 2AB"
}
],
"total": 2
}
Disambiguation matters. A name search often returns several companies. Use the extra fields to choose:
-
Prefer an
ActivestatusoverDissolvedorLiquidationunless you specifically want a historical entity. - Match the
addressagainst what you already know about the company. - If you can't confidently pick one, surface the candidates to a human rather than guessing.
Search returns a single page of up to pageSize results, ranked by relevance. total
tells you how many matched overall. Narrow a broad search with a more specific q rather
than expecting to page through thousands of results.
Take the companyNumber of your chosen match and pass it — along with the same
jurisdiction — to the profile endpoint.
curl -s -H "Authorization: Bearer $TOKEN" \
"https://public-api.heygrand.com/v1/profile/12345678?jurisdiction=GB"
{
"name": "Acme Trading Ltd",
"registrationNumber": "12345678",
"jurisdiction": "GB",
"tradingStatus": "ACTIVE"
}
Production notes
Before you ship this, handle the cases the happy path skips:
-
No results. An empty
resultsarray means nothing matched. Don't index intoresults[0]without checking. -
Company not found. A profile lookup for a number that doesn't exist returns
404. This can happen if you pass the wrongjurisdictionfor the number. -
Token expiry. A
401on search/profile means your cached token expired — request a new one and retry once. - Rate limits. Back off on
429. See Rate limits. -
Credentials. Read
client_id/client_secretfrom the environment or a secrets manager — never hard-code them.
Complete script
A single, runnable Bash script that ties it together and handles the empty-result and not-found cases:
#!/usr/bin/env bash
set -euo pipefail
BASE_URL="https://public-api.heygrand.com"
QUERY="${1:?usage: company-lookup.sh <name> [jurisdiction]}"
JURISDICTION="${2:-GB}"
# 1. Authenticate
TOKEN=$(curl -s -X POST "$BASE_URL/v1/oauth/token" \
-H 'Content-Type: application/json' \
-d "{\"grant_type\":\"client_credentials\",\"client_id\":\"$GRAND_CLIENT_ID\",\"client_secret\":\"$GRAND_CLIENT_SECRET\"}" \
| jq -r '.access_token')
# 2. Search and take the first match
NUMBER=$(curl -s -H "Authorization: Bearer $TOKEN" \
"$BASE_URL/v1/search?q=$(jq -rn --arg q "$QUERY" '$q|@uri')&jurisdiction=$JURISDICTION&pageSize=10" \
| jq -r '.results[0].companyNumber // empty')
if [[ -z "$NUMBER" ]]; then
echo "No companies found for '$QUERY' in $JURISDICTION" >&2
exit 1
fi
# 3. Fetch the full profile (explicitly handle 404)
HTTP_CODE=$(curl -s -o /tmp/profile.json -w "%{http_code}" \
-H "Authorization: Bearer $TOKEN" \
"$BASE_URL/v1/profile/$NUMBER?jurisdiction=$JURISDICTION")
if [[ "$HTTP_CODE" == "404" ]]; then
echo "Company '$NUMBER' not found in jurisdiction '$JURISDICTION'" >&2
exit 1
fi
jq . /tmp/profile.json
Run it:
export GRAND_CLIENT_ID="your-client-id"
export GRAND_CLIENT_SECRET="your-client-secret"
./company-lookup.sh "Acme Trading" GB