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:

  1. AuthenticatePOST /v1/oauth/token → an access token
  2. SearchGET /v1/search with that token → a companyNumber
  3. FetchGET /v1/profile/{companyNumber} → the full profile

Prerequisites

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 Active status over Dissolved or Liquidation unless you specifically want a historical entity.
  • Match the address against 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 results array means nothing matched. Don't index into results[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 wrong jurisdiction for the number.
  • Token expiry. A 401 on 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_secret from 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

See also

Every status code and how to recover from it. Full schemas for search and profile responses.

Grand Public API