Map API

Turn incoming data into the exact shape your application expects. Give Map your source records plus the target schema you need, and it infers the field mapping, converts every value to the target type, and returns data in your shape with a confidence and a plain-language reason for each mapping. You dont write dozens of field-mapping rules by hand.

Map is deterministic and reuses Zapinners shared data-intelligence primitives (field-name concepts, value coercion) and, optionally, the Repair engine. It is billed once per input record, even when repair-before-mapping runs.

Endpoint & cost

POST /api/v1/map

Metering is based on input records: 100 records sent to Map draws down 100 records, regardless of how many fields are mapped or whether repair-first runs. The synchronous maximum is 500 records per request. Requests that fail validation are never charged.

How mapping works

For each field in your target schema, Map picks the best source mapping:

  • Direct match the source field name equals the target name (case-insensitive).
  • Alias match both names represent the same business concept (e.g. annualrevenue and annual_revenue).
  • Field similarity a conservative name-overlap fallback.
  • Combine a full-name target fed by separate first_name + last_name columns.
  • Safe split a first/last-name target derived from a single full-name column.

The matched value is then converted to the target type (currency and numbers parsed, dates to ISO 8601, phones to E.164, emails normalized, booleans parsed). Low-confidence mappings are not applied silently they go to review_required.

Request fields

FieldTypeDescription
records*object[]Source records to transform (1-500). Values must be primitives (no nested objects/arrays).
target_schema*objectThe schema you want. Each value is a type string ("string") or an object { type, required }. Types: string, integer, float, number, boolean, email, phone, url, date, datetime, currency, percentage, company, name, country, state, postal_code, identifier.
options.strictbooleanOnly apply very high-confidence mappings; ambiguous ones go to review. Default false.
options.repair_before_mappingbooleanClean and standardize values with the Repair engine before mapping. Billed once. Default false.
options.drop_unmappedbooleanDrop source fields that do not map to any target field. Default true.

Response

FieldTypeDescription
dataobject[]Your records in the target shape, one per input record.
mappingsobject[]How each target field was resolved: source_fields, target_field, transformation, confidence, and reason.
unmapped_source_fieldsstring[]Source fields that did not feed any target field.
unresolved_target_fieldsstring[]Target fields no source could confidently populate.
review_requiredobject[]Low-confidence mappings, unresolved required fields, and per-record conversion issues.
summaryobjectCounts: records_received/mapped, records_with_review_items, fields_mapped, unresolved_fields, plus the flags used.

Strict mode

Set options.strict: true to only auto-apply mappings at very high confidence. Anything less certain is left unresolved and surfaced in review_required rather than guessed use it when a wrong mapping is worse than a missing one.

Repair before mapping

Set options.repair_before_mapping: true to run the Repair engine over your records first (normalize and standardize values, without changing the row count), then map the cleaned records. This is billed once per input record you are never charged separately for the internal repair.

Errors & limits

  • 400 invalid_request empty records, a nested/non-primitive value, or an empty target_schema.
  • 400 invalid_request a target field declares an unknown type.
  • 413 payload_too_large the body exceeds the size cap or more than 500 records.
  • 401 unauthorized / invalid_api_key missing or invalid key.
  • 429 usage_limit_exceeded / rate_limit_exceeded allowance or burst limit reached.

Internal errors return a sanitized 500 internal_error and never leak record contents. See Errors.

Try it live

Pick a scenario (map, strict, or repair-first) or edit the JSON, then send it against the live engine. No API key required.

Try it livePOST /api/v1/map
Request bodyeditable
Response

// Send the request to see the live response.

Live demo endpoint · no API key needed · rate-limited · not stored · runs the production engine

Example request

Request
POST /api/v1/map
Authorization: Bearer zap_live_...
Content-Type: application/json

{
  "records": [
    { "firstname": "Mike", "lastname": "Perkins", "annualrevenue": "$2.4M", "phone": "(704) 555-1234", "company": "Acme, LLC" }
  ],
  "target_schema": {
    "full_name": { "type": "string", "required": true },
    "company_name": { "type": "string" },
    "annual_revenue": { "type": "number" },
    "phone_number": { "type": "phone" }
  },
  "options": { "strict": false, "repair_before_mapping": false }
}

Example response

200 OK
{
  "request_id": "req_xxx",
  "success": true,
  "data": [
    { "full_name": "Mike Perkins", "company_name": "Acme, LLC", "annual_revenue": 2400000, "phone_number": "+17045551234" }
  ],
  "mappings": [
    { "source_fields": ["firstname", "lastname"], "target_field": "full_name", "target_type": "string", "transformation": "concatenate_with_space", "confidence": 0.98, "reason": "target expects a full name and the source provides separate first and last name columns" },
    { "source_fields": ["company"], "target_field": "company_name", "target_type": "string", "transformation": "alias_match", "confidence": 0.95, "reason": "source field \"company\" and target \"company_name\" both represent \"company\"" },
    { "source_fields": ["annualrevenue"], "target_field": "annual_revenue", "target_type": "number", "transformation": "alias_match", "confidence": 0.95, "reason": "source field \"annualrevenue\" and target \"annual_revenue\" both represent \"revenue\"" },
    { "source_fields": ["phone"], "target_field": "phone_number", "target_type": "phone", "transformation": "alias_match", "confidence": 0.95, "reason": "source field \"phone\" and target \"phone_number\" both represent \"phone\"" }
  ],
  "unmapped_source_fields": [],
  "unresolved_target_fields": [],
  "review_required": [],
  "summary": {
    "records_received": 1,
    "records_mapped": 1,
    "records_with_review_items": 0,
    "fields_mapped": 4,
    "unresolved_fields": 0,
    "repaired_before_mapping": false,
    "strict": false
  },
  "usage": { "records_processed": 1, "credits_used": 1, "credits_remaining": 999 }
}

Code examples

curl -X POST https://zapinner.com/api/v1/map \
  -H "Authorization: Bearer $ZAPINNER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "records": [
      { "firstname": "Mike", "lastname": "Perkins", "annualrevenue": "$2.4M", "phone": "(704) 555-1234", "company": "Acme, LLC" }
    ],
    "target_schema": {
      "full_name": { "type": "string", "required": true },
      "company_name": { "type": "string" },
      "annual_revenue": { "type": "number" },
      "phone_number": { "type": "phone" }
    }
  }'
  • Repair fix messy data you already have (Map can run it first).
  • Guard validate, repair, or reject data before it reaches your app.
  • Normalize the value-standardization primitive underneath.