Skip to documentation content
HintadataDocs

First product search

Resolve a canonical product ID and inspect its current offers. Start with a sandbox key with catalog:read. The browser exercise uses isolated representative data and needs no key.

In this guide
  1. 1Prerequisites
  2. 2Run and verify
  3. 3Recover safely
In this guide
  1. 1Prerequisites
  2. 2Run and verify
  3. 3Recover safely

Prerequisites and safety

Resolve a canonical product ID and inspect its current offers. Start with a sandbox key with catalog:read. The browser exercise uses isolated representative data and needs no key.

Run the isolated exercise

Search, recover and inspect
  1. 1
    Reject an invalid limit
  2. 2
    Find product identities
  3. 3
    Inspect the resolved product

Run from your server

bashillustrative
Example trust: illustrativeShows the shape or idea; do not use it unchanged in production.
set -euo pipefail
# Requires bash, curl and jq. Use an isolated sandbox API key.
DOCS_BASE_URL="${DOCS_BASE_URL:-https://hintadata.fi}"
: "${HINTADATA_API_KEY:?Set a sandbox API key privately}"
DOCS_VARIABLES=$(jq -cn  '{}')
DOCS_RESPONSE_FILE=$(mktemp)
trap 'rm -f -- "$DOCS_RESPONSE_FILE"' EXIT
# invalid: expected HTTP 400
DOCS_PATH="$(jq -cnr --argjson vars "$DOCS_VARIABLES" '"/v1/search?q=sony&limit=0"')"
DOCS_STATUS=$(curl --silent --show-error --max-time 20 --output "$DOCS_RESPONSE_FILE" --write-out '%{http_code}' -X 'GET' "$DOCS_BASE_URL$DOCS_PATH" -H "Authorization: Bearer $HINTADATA_API_KEY")
[[ "$DOCS_STATUS" == '400' ]] || { printf '%s\n' 'Unexpected HTTP status at invalid' >&2; exit 1; }
DOCS_RESPONSE=$(jq -c . "$DOCS_RESPONSE_FILE")
jq -en --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" --argjson status "$DOCS_STATUS" '((($response | if type == "object" or type == "array" then has("error") else false end) and ($response["error"] | if type == "object" or type == "array" then has("code") else false end))) and ($response["error"]["code"] == ("invalid_request"))' >/dev/null
printf '%s\n' 'invalid: verified'
# search: expected HTTP 200
DOCS_PATH="$(jq -cnr --argjson vars "$DOCS_VARIABLES" '"/v1/search?q=sony&limit=2"')"
DOCS_STATUS=$(curl --silent --show-error --max-time 20 --output "$DOCS_RESPONSE_FILE" --write-out '%{http_code}' -X 'GET' "$DOCS_BASE_URL$DOCS_PATH" -H "Authorization: Bearer $HINTADATA_API_KEY")
[[ "$DOCS_STATUS" == '200' ]] || { printf '%s\n' 'Unexpected HTTP status at search' >&2; exit 1; }
DOCS_RESPONSE=$(jq -c . "$DOCS_RESPONSE_FILE")
jq -en --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" --argjson status "$DOCS_STATUS" '((($response | if type == "object" or type == "array" then has("query") else false end))) and ($response["query"] == ("sony"))' >/dev/null
jq -en --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" --argjson status "$DOCS_STATUS" '((($response | if type == "object" or type == "array" then has("items") else false end) and ($response["items"] | if type == "object" or type == "array" then has(0) else false end) and ($response["items"][0] | if type == "object" or type == "array" then has("canonical_product_id") else false end)) == true)' >/dev/null
DOCS_VARIABLES=$(jq -cn --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" '$vars + {"productId":$response["items"][0]["canonical_product_id"],"secondId":$response["items"][1]["canonical_product_id"]}')
printf '%s\n' 'search: verified'
# inspect: expected HTTP 200
jq -e 'has("productId")' <<< "$DOCS_VARIABLES" >/dev/null
DOCS_PATH="$(jq -cnr --argjson vars "$DOCS_VARIABLES" '"/v1/products/" + ($vars["productId"] | tostring | @uri) + ""')"
DOCS_STATUS=$(curl --silent --show-error --max-time 20 --output "$DOCS_RESPONSE_FILE" --write-out '%{http_code}' -X 'GET' "$DOCS_BASE_URL$DOCS_PATH" -H "Authorization: Bearer $HINTADATA_API_KEY")
[[ "$DOCS_STATUS" == '200' ]] || { printf '%s\n' 'Unexpected HTTP status at inspect' >&2; exit 1; }
DOCS_RESPONSE=$(jq -c . "$DOCS_RESPONSE_FILE")
jq -en --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" --argjson status "$DOCS_STATUS" '((($response | if type == "object" or type == "array" then has("canonical_product_id") else false end))) and ($response["canonical_product_id"] == ($vars["productId"]))' >/dev/null
jq -en --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" --argjson status "$DOCS_STATUS" '((($response | if type == "object" or type == "array" then has("offers") else false end) and ($response["offers"] | if type == "object" or type == "array" then has(0) else false end) and ($response["offers"][0] | if type == "object" or type == "array" then has("delivered_price") else false end)) == true)' >/dev/null
jq -en --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" --argjson status "$DOCS_STATUS" '((($response | if type == "object" or type == "array" then has("canonical_product_id") else false end))) and ($response["canonical_product_id"] == ($vars["productId"]))' >/dev/null
printf '%s\n' 'inspect: verified'

Verify the result

Expect HTTP 200 with query, items, total and next_offset. Save canonical_product_id, not a display name. An empty items array is a valid search result: refine q before reading an ID. X-Data-Mode: demo is not live evidence.

Recovery and cleanup

400 invalid_request: set q to 1–120 characters and limit to 1–100. 401: replace the invalid key; 403: check scope and subscription. 503 source_unavailable/source_stale: ask the source owner to publish fresh data. Do not present preview data as a live fallback.

For 429 or temporary 5xx, respect Retry-After and use bounded retries. Keep API keys and response bodies out of logs and support tickets.

Optional live task measurement

Explicit consent records a start and the first successful live search in the same authenticated workspace and environment. The receipt expires after 30 minutes and grants no API access. Reading, sandbox runs and test-key requests never count as live success. Only task, locale, release, timestamps and an opaque correlation reference are sent to analytics; no search text, email, key or response body. Omit both measurement headers to opt out. If measurement is unavailable, continue with the normal search command above.

bashillustrative
Example trust: illustrativeShows the shape or idea; do not use it unchanged in production.
set -euo pipefail
# Optional: explicit analytics consent; use a LIVE key only when ready for live data.
ATTEMPT=$(curl --fail-with-body --silent --show-error https://hintadata.fi/api/docs/tasks \
  -H "Authorization: Bearer $HINTADATA_API_KEY" -H 'X-Analytics-Consent: granted' \
  -H 'Content-Type: application/json' \
  --data '{"task":"first-product-search","locale":"en"}' | jq -er '.receipt')
curl --fail-with-body --silent --show-error --get https://hintadata.fi/v1/search \
  -H "Authorization: Bearer $HINTADATA_API_KEY" -H 'X-Analytics-Consent: granted' \
  -H "X-Docs-Attempt: $ATTEMPT" --data-urlencode 'q=sony' --data 'limit=2' | jq .
unset ATTEMPT
Was this page helpful?