Migrating from /v1 to /v2
For an integration currently calling /v1/analyze or /v1/suggest. There is no fixed deadline — /v1 keeps working exactly as it does today for as long as you need it — but new work should target /v2, and this is everything that changes.
1. The base path
https://app.hitsonar.com/v1/analyze becomes https://app.hitsonar.com/v2/analyze, and the same for /suggest and /health. Nothing else about the request changes: same method, same headers, same request body shape, same authentication, same successful response body shape.
2. The error shape, on failure only
This is the one breaking change, and it only matters if your code inspects an error response.
| Field | Type | Meaning |
|---|---|---|
Before | /v1 | { "error": "unauthorized" } |
After | /v2 | { "error": { "code": "unauthorized", "message": "unauthorized", "type": "authentication_error", "param": null, "requestId": "req_..." } } |
If your code today does something like if (body.error === "unauthorized") or raise Exception(body["error"]), change it to read body.error.code and body.error.message instead. The situations that produce an error haven’t changed — same status codes, same meanings — only the shape of the object describing which one happened. The old 429 usage-limit body ({ error, reason, used, limit, resetsAt } at the top level) is now { error: { code, ... }, usage: { used, limit, resetsAt } } — the same fields, nested under usage instead of flattened.
3. A request id on every response
X-Request-Id (also inside error.requestId on a failure) is now on every /v2 response, success or error. Not required reading, but worth logging — it’s what support will ask for.
4. Rate-limit headers on organization-key responses
X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset (60 requests/minute per key by default) are sent on /v2 responses to an organization API key that has a rate-limit state, not only when you’re throttled — so you can see a limit coming. They are absent for operator-configured keys, which have no organization counter, and absent on a 401, which has no identified key to report a state for. On a throttle rejection, Retry-After is the authoritative header for how long to wait.
5. Usage headers on /v2/analyze
X-Usage-Limit, X-Usage-Remaining, X-Usage-Reset report the monthly analysis allowance without a separate call, on organization-metered /v2/analyze responses. Optional to use, and absent where no organization allowance was consulted.
6. The OpenAPI document moved
/v1/openapi.json still describes /v1 exactly as it always has. The current, actively maintained spec is now at /v2/openapi.json (and /v2/openapi.yaml) — point any code generator or Postman collection at the new URL.
What does not change
- Authentication: same key, same header.
- Request bodies and query parameters: identical shapes.
- Successful response bodies: identical shapes — nothing renamed or removed.
- Status codes: same codes for the same situations.
- Rate and usage limits themselves: same numbers, just now visible in headers.
- CORS: enabled the same way on both.
Suggested steps
- Change the base path from
/v1to/v2. - Update error handling to read
error.code/error.messagefrom the nested object. - If you parse the 429 usage-limit body, read it from
usage.*instead of the top level. - Optional: log
X-Request-Idfrom responses. - Run your existing tests (or a few manual calls) against
/v2before flipping production traffic. - Done — there’s nothing to turn off, and no rush on
/v1.
Using the SDKs instead
@hitsonar/sdk (npm) and hitsonar (PyPI) wrap all of the above — typed models, error classes, automatic rate-limit/usage tracking — already pointed at /v2. See the TypeScript SDK guide and the Python SDK guide.
Verifying a published SDK works from a clean install
Once a package is live on its registry, the release isn’t done until the actual install path is proven, not just that the registry accepted the upload:
mkdir /tmp/hitsonar-check && cd /tmp/hitsonar-check
npm init -y && npm install @hitsonar/sdk
node -e "import('@hitsonar/sdk').then(async ({HitSonar}) => {
const c = new HitSonar({apiKey: process.env.HITSONAR_API_KEY});
console.log(await c.suggest('gospel blues'));
})"python3 -m venv /tmp/hitsonar-check-py && /tmp/hitsonar-check-py/bin/pip install hitsonar
/tmp/hitsonar-check-py/bin/python -c "
import os
from hitsonar import HitSonar
c = HitSonar(api_key=os.environ['HITSONAR_API_KEY'])
print(c.suggest('gospel blues'))
"Both should print a real suggestion list. If either fails, the package isn’t actually usable yet, whatever the registry page says.
Questions
The developers reference is the live, current contract, and the changelog has the dated history of every change to either.
