Batch scoring
API reference · POST /v1/score
Score records in real-time using a trained model.
Once a model is trained, this is the call an integration or an agent makes in normal operation. It scores up to 1000 records per call and returns one row per input record, in order.
Request
POST https://api.ax1om.ai/v1/scoreAuthorize with your API key. Keys are minted in the app and the format is documented on authentication.
Parameters
| Name | In | Type | Required |
|---|---|---|---|
authorization | header | string | No |
Body
ScoreRequest, required. JSON.
| Field | Type | Required | Description |
|---|---|---|---|
explain | boolean | No | Opt in to per-record drivers and top_reason_1..3 strings. Adds a few ms; off by default. |
model_id | string | Yes | The trained model to score against (mdl_...). |
records | array<object> | Yes | Up to 1000 records. Each is a flat object of your CRM field values. Extra fields are allowed; unknown fields are ignored by the model. |
Response
| Status | Body | When |
|---|---|---|
200 | V1ScoreResponse | Records scored. |
400 | V1Error | Bad request (code: bad_request), or more than 1000 records in one batch (code: record_limit_exceeded). |
401 | V1Error | Missing or invalid API key (code: invalid_api_key). |
402 | V1Error | The org's trial ended and it has no active subscription, so it is in read-only mode (code: expired_read_only). Read access to existing scores, models, and factors is unaffected; API scoring resumes on upgrade. Unlike a 429 quota, this does not clear at the next billing period, so do not retry on a timer. |
403 | V1Error | Live API is turned off for the score that owns this model (code: api_disabled). Turn Live API on in that score's row under Activate. Retrying will not clear it. |
404 | V1Error | Model not found (code: model_not_found). |
409 | V1Error | Your org's deploy policy is set to block, and this model has not passed its validation gate (code: deploy_blocked). The message names the gate and the override path. Retrying will not clear it: either the model clears its gate as its predictions mature, or an admin lifts the refusal. |
422 | V1Error | Malformed request body (code: bad_request). |
429 | V1Error | Rate limited (code: rate_limited), or monthly scored-record quota exhausted past the backstop, including plans without API scoring (code: quota_exceeded). |
500 | V1Error | Internal error (code: internal_error). |
V1ScoreResponse
Batch scoring response.
| Field | Type | Always present | Description |
|---|---|---|---|
cache_hits | integer | No | How many records matched your existing scored data. |
model_id | string | Yes | |
records_scored | integer | No | Count of records scored in this call. |
scores | array<V1ScoreRow> | Yes | |
test | boolean | No | Present and true only for test keys (ax1m_sk_test_...). Never billed. |
usage | V1Usage | Yes | |
validation | V1Validation | Yes |
V1ScoreRow
One scored record. Order matches the request `records` array.
| Field | Type | Always present | Description |
|---|---|---|---|
conversion_likelihood | number (float) | Yes | Model conversion likelihood, 0-1 (4 dp). |
crm_id | string | No | Echoed back when supplied on the input record. |
delta | integer | No | score minus previous_score, when a previous score exists. |
drivers | array<object> | No | Per-record feature contributions. Present only when explain=true. |
enriched | boolean | Yes | true only when the record matched your existing scored data. false = thin score from the fields you sent (cold record). |
previous_score | integer | No | Prior score for this crm_id, when one exists. |
score | integer | Yes | Fit score, 0-100. |
top_reason_1 | string | null | No | Top driver string. Present only when explain=true. |
top_reason_2 | string | null | No | Second driver. Present only when explain=true. |
top_reason_3 | string | null | No | Third driver. Present only when explain=true. |
warnings | array<string> | No | Present only when the record trips the recency age guard. Metadata only; the score is not changed. |
V1Usage
Monthly metered usage for the API key's org. The live monthly cap is a soft cap: paid live keys keep scoring past `limit` in a grace band until a hard abuse backstop; test keys and plans without API scoring keep a hard cap.
| Field | Type | Always present | Description |
|---|---|---|---|
backstop | integer | null | Yes | Hard stop for this billing month. Scoring pauses (429, code quota_exceeded) once usage reaches it. null for test keys. |
current | integer | Yes | Records scored so far this billing month, including this call. |
in_grace | boolean | Yes | true once the org is scoring past its included monthly limit, in the grace band. |
limit | integer | Yes | Monthly included scored-record limit for the plan (test keys meter into a separate free bucket). |
remaining | integer | Yes | Records left this month. Goes negative in the grace band once usage passes the included limit. |
V1Validation
Whether this model has earned the act, computed server-side from the same validation readout the check-in renders. Present on every batch-scoring response. Surfacing only by default: your org's deploy policy decides whether a not-passed model is refused (409 deploy_blocked) or scored with this object attached.
| Field | Type | Always present | Description |
|---|---|---|---|
failing_gate | "predictions_recorded" | "cohort_reconciles" | "matured_cohort" | "outcomes_observed" | "rate_rises_with_band" | "top_band_lift" | "null" | null | Yes | The gate that was not cleared. Null when state is passed, unresolved or not_applicable. |
state | "passed" | "failed" | "too_early_to_grade" | "unresolved" | "not_applicable" | Yes | passed: every gate cleared. failed: a gate was reached and did not clear. too_early_to_grade: a gate could not be reached yet - a young model, not a bad one. unresolved: the state could not be read on our side; nothing is claimed about the model. not_applicable: this model family is not graded by the forward-cohort gates. |
summary | string | Yes | One human sentence naming the state and the gate. |
Example
The same call in three clients. Pick one and the choice follows you across every panel on the documentation that offers it.
curl -X POST "https://api.ax1om.ai/v1/score" \ -H "Authorization: Bearer ax1m_sk_your_key" \ -H "Content-Type: application/json" \ -d '{ "model_id": "mdl_abc123", "records": [ { "title": "VP Sales", "industry": "SaaS", "employees": 250 } ] }'import requests
response = requests.post( "https://api.ax1om.ai/v1/score", headers={"Authorization": "Bearer ax1m_sk_your_key"}, json={ "model_id": "mdl_abc123", "records": [ { "title": "VP Sales", "industry": "SaaS", "employees": 250 } ] },)response.raise_for_status()print(response.json())const response = await fetch("https://api.ax1om.ai/v1/score", { method: "POST", headers: { "Authorization": "Bearer ax1m_sk_your_key", "Content-Type": "application/json", }, body: JSON.stringify({ "model_id": "mdl_abc123", "records": [ { "title": "VP Sales", "industry": "SaaS", "employees": 250 } ] }),});
if (!response.ok) { const { error } = await response.json(); throw new Error(`${error.code}: ${error.message}`);}
console.log(await response.json());{ "model_id": "mdl_abc123", "scores": [ { "score": 82, "conversion_likelihood": 0.823, "enriched": false } ], "records_scored": 1, "cache_hits": 0, "usage": { "current": 151, "limit": 10000, "remaining": 9849, "in_grace": false, "backstop": 20000 }}Errors
Every failure returns the /v1 envelope. Branch onerror.code, never on the HTTP status or the message text. The codes below are the ones the contract declares for this operation; the full table with prose for each is on theindex.
| Status | Codes |
|---|---|
400 | record_limit_exceeded · bad_request |
401 | invalid_api_key |
402 | expired_read_only |
403 | api_disabled |
404 | model_not_found |
409 | deploy_blocked |
422 | bad_request |
429 | quota_exceeded · rate_limited |
500 | internal_error |
Notes
- Records that match your existing scored data come back enriched with a delta; unmatched records are scored from the fields you send.
- Batch up to 1000 records per call to stay well under the rate limit.
- The score is a 0-100 ranking of conversion likelihood. It is not a percentage chance of conversion, and the per-record factors do not sum to it.