Geocode Batch Plan and Normalize
data.geocode_batch_stub
Overview
Two services over the provider profile you declare, and neither one sends anything. Under PLAN you get the requests you would send: batched addresses, substituted and percent-encoded URLs, query parameters or a form body, and pacing arithmetic derived from your own rate-limit hints. Under NORMALIZE you hand back the responses your client received and get one canonical shape: an address index, a coordinate, and a precision from a closed eight-member scale. There is no apiKey, token or headers field in this schema at all and the profile key set is closed, so a credential cannot arrive under an unfamiliar name and end up in a plan that gets logged, diffed and pasted into a ticket.
The two halves of a geocoding integration that do not need the network are the two halves that are easy to get wrong.
Reliability
The same request always gives the same plan or the same normalised answers, on any machine and under any locale, time zone or default charset: percent-encoding is written out over UTF-8 bytes and credential name matching folds at the root locale, so neither moves with the host. projectedWallClockMs is (requestCount - 1) x minIntervalMs, arithmetic over the hints you supplied rather than a measurement, because this skill reads no clock. Under NORMALIZE a malformed entry is per-entry and a miscounted one is fatal: an entry that does not parse, that lacks the array at resultPath, or that carries a latitude outside -90 to 90 or a longitude outside -180 to 180 lands in rejected[] and the call still succeeds with partial true, because one bad provider row must not discard 999 good ones and a coordinate is never clamped; but a response whose result count disagrees with the addresses of the request it answers is refused outright, since positional realignment is the one error that attaches the right coordinates to the wrong address. A provider precision absent from precisionMapping normalises to UNKNOWN rather than into a neighbouring band. Fifteen refusals, none retryable unchanged: GEOCODE_MODE_UNSUPPORTED when mode is not PLAN or NORMALIZE, so choose one of those two values; GEOCODE_ADDRESSES_INVALID, GEOCODE_ADDRESS_LIMIT_EXCEEDED above 1000 and GEOCODE_ADDRESS_TOO_LONG above 1024 code points, so shorten or split the address list before retrying; GEOCODE_PROFILE_INVALID and GEOCODE_PROFILE_KEY_UNSUPPORTED for the profile, whose key set is closed; GEOCODE_CREDENTIAL_FORBIDDEN when a parameter name matches the credential vocabulary in staticParameters, in parameterMapping or already inside the endpoint query; GEOCODE_ENDPOINT_INVALID for a non-https endpoint, a missing host or userinfo; GEOCODE_PLACEHOLDER_UNSUPPORTED outside the four placeholders; GEOCODE_PARAMETER_UNSUPPORTED outside the request vocabulary or for a role nothing supplies; GEOCODE_BATCH_SIZE_INVALID outside 1 to 100, so set a supported provider batch size; GEOCODE_RESPONSES_REQUIRED and GEOCODE_RESPONSES_FORBIDDEN when the mode and the payload disagree; GEOCODE_RESPONSE_CARDINALITY_MISMATCH when response counts cannot align positionally, so supply exactly one response per planned request with one result per address; and GEOCODE_OUTPUT_TOO_LARGE when the plan or the answers would not fit the inline response ceiling.
Context footprint
A plan is roughly one URL per batch; normalised answers are one small object per address, with any formatted address cut at 256 code points. A successful call costs 1 execution credit.
When to use it
When you want the request plan, the pacing arithmetic or one canonical answer shape, and you will do the sending yourself.
When you expected a coordinate for an address — nothing here looks one up.
Scope
Non-goals
- Does not perform any network call, in either mode
- Does not accept, store or forward a credential of any kind
- Does not geocode an address itself or hold any gazetteer
- Does not clamp a coordinate that falls outside its valid range
- Does not guess a precision a provider did not declare
Properties
Capabilities
Cost & billing
Flat rate per invocation
File handling
How we handle your data
What this means
This skill may receive sensitive content and may return submitted, transformed, extracted, or reformatted content.
Squillify guarantees
- Runs inside Squillify
- This skill runs inside Squillify.
- Payloads are not sent to an external service by this skill.
- Squillify does not permanently store submitted payloads.
- Telemetry records metadata only, not submitted content.
- Submitted payloads are not used for training.
- Submitted payloads are not reviewed by humans.
- This skill does not target sensitive data.
- The response may contain submitted or transformed content.
Your responsibility
Squillify does not determine whether submitted content is sensitive. Your application is responsible for deciding what data it is allowed to submit. Your application is responsible for securing the response.
Input schema
| Field | Type | Required | Description |
|---|---|---|---|
| mode | string | No | PLAN builds requests; NORMALIZE maps responses; omit it for PLAN. |
| addresses | array | Yes | Bounded array of 1 through 1000 address strings whose indexes are reused on every planned request. |
| providerProfile | object | Yes | Closed nine-key provider profile; extra keys are refused so a credential cannot hide under an unfamiliar name. |
| providerResponses | array | No | Bounded array of provider JSON strings, one per planned request. Required unless mode is PLAN. |
Output schema
| Field | Type | Required | Description |
|---|---|---|---|
| requests | array | No | Bounded array of 0 through 1000 closed planned requests; PLAN only. |
| pacing | object | No | Closed pacing projection derived from your rate-limit hints, not a measurement. |
| normalized | array | No | Bounded array of 0 through 1000 closed canonical answers; NORMALIZE only. |
| rejected | array | No | Bounded array of 0 through 1000 closed rejections; NORMALIZE only. |
| summary | object | Yes | Closed PLAN or NORMALIZE summary; PLAN uses addressCount and requestCount, NORMALIZE uses requested and match counts. |
API contract
POST /v1/executecurl -sS -X POST "$BASE/v1/execute" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{"skillKey":"data.geocode_batch_stub","input":{"addresses":["Dam 1, Amsterdam"],"providerProfile":{"endpointTemplate":"https://api.example.invalid/v1/search?q={query}"}}}'import { Squillify } from "@squillify/sdk";
const sq = new Squillify({ apiKey: process.env.SQUILLIFY_API_KEY });
const { output } = await sq.execute("data.geocode_batch_stub", {"addresses":["Dam 1, Amsterdam"],"providerProfile":{"endpointTemplate":"https://api.example.invalid/v1/search?q={query}"}});Errors
| Code | HTTP | Retryable | Description |
|---|---|---|---|
| VALIDATION_ERROR | 400 | No | The request payload failed schema validation. |
| UNAUTHORIZED | 401 | No | Missing or invalid API key. |
| SKILL_NOT_ACCESSIBLE | 403 | No | The API key is not granted access to this skill. |
| SKILL_NOT_AVAILABLE_FOR_PLAN | 403 | No | This skill is not included in your current plan. |
| PLAN_ELIGIBILITY_UNAVAILABLE | 503 | Yes | Plan eligibility could not be decided. Retry after backoff. |
| SKILL_NOT_FOUND | 404 | No | The requested skill or version does not exist. |
| TENANT_RATE_LIMITED | 429 | Yes | Request rate limit exceeded. Retry after backoff. |
| QUOTA_EXCEEDED | 429 | Yes | Plan quota exhausted. Retryable once the quota period resets. |
| EXECUTION_TIMEOUT | 504 | Yes | Execution exceeded the allowed time budget. |
| GEOCODE_MODE_UNSUPPORTED | 400 | No | The skill does not support the requested variant. |
| GEOCODE_ADDRESSES_INVALID | 400 | No | The request payload failed this skill's input validation. |
| GEOCODE_ADDRESS_LIMIT_EXCEEDED | 429 | No | A size, quota, rate, or time limit was exceeded. |
| GEOCODE_ADDRESS_TOO_LONG | 429 | No | A size, quota, rate, or time limit was exceeded. |
| GEOCODE_PROFILE_INVALID | 400 | No | The request payload failed this skill's input validation. |
| GEOCODE_PROFILE_KEY_UNSUPPORTED | 400 | No | The skill does not support the requested variant. |
| GEOCODE_CREDENTIAL_FORBIDDEN | 403 | No | The operation is forbidden by policy or permissions. |
| GEOCODE_ENDPOINT_INVALID | 400 | No | The request payload failed this skill's input validation. |
| GEOCODE_PLACEHOLDER_UNSUPPORTED | 400 | No | The skill does not support the requested variant. |
| GEOCODE_PARAMETER_UNSUPPORTED | 400 | No | The skill does not support the requested variant. |
| GEOCODE_BATCH_SIZE_INVALID | 400 | No | The request payload failed this skill's input validation. |
| GEOCODE_RESPONSES_REQUIRED | 400 | No | The request payload failed this skill's input validation. |
| GEOCODE_RESPONSES_FORBIDDEN | 400 | No | The request payload failed this skill's input validation. |
| GEOCODE_RESPONSE_CARDINALITY_MISMATCH | 400 | No | The request payload failed this skill's input validation. |
| GEOCODE_OUTPUT_TOO_LARGE | 429 | No | A size, quota, rate, or time limit was exceeded. |
Other limits
| Limit | Value |
|---|---|
| Recommended inline bytes | 32 KiB |
Limitations
- Does not perform any network call, in either mode
- Does not accept, store or forward a credential of any kind
- Does not geocode an address itself or hold any gazetteer
- Does not clamp a coordinate that falls outside its valid range
- Does not guess a precision a provider did not declare
- Actually calling a geocoding provider
- Anything requiring an API key to be supplied here
How to call
POST /v1/execute (requires authentication)
{
"skillKey": "data.geocode_batch_stub",
"input": {
"addresses": [
"Dam 1, Amsterdam"
],
"providerProfile": {
"endpointTemplate": "https://api.example.invalid/v1/search?q={query}"
}
}
}Examples
A single-address plan
{
"addresses": [
"Dam 1, Amsterdam"
],
"providerProfile": {
"endpointTemplate": "https://api.example.invalid/v1/search?q={query}"
}
}{
"pacing": {
"projectedWallClockMs": 0,
"requestCount": 1,
"burst": 1,
"requestsPerSecond": 1,
"batchSize": 1,
"minIntervalMs": 0
},
"summary": {
"truncated": false,
"addressCount": 1,
"requestCount": 1
},
"requests": [
{
"method": "GET",
"index": 0,
"url": "https://api.example.invalid/v1/search?q=Dam%201%2C%20Amsterdam",
"queryParameters": [],
"addressIndexes": [
0
]
}
]
}Normalize one GeoJSON provider response
{
"providerProfile": {
"endpointTemplate": "https://api.example.invalid/v1/search?q={query}",
"precisionMapping": {
"house": "ROOFTOP"
}
},
"providerResponses": [
"{\"features\":[{\"geometry\":{\"coordinates\":[4.8952,52.3702]},\"properties\":{\"precision\":\"house\",\"formatted\":\"Dam 1, Amsterdam\",\"components\":{\"road\":\"Dam\",\"city\":\"Amsterdam\"}}}]}"
],
"addresses": [
"Dam 1, Amsterdam"
],
"mode": "NORMALIZE"
}{
"normalized": [
{
"longitude": 4.8952,
"matched": true,
"formatted": "Dam 1, Amsterdam",
"addressIndex": 0,
"latitude": 52.3702,
"componentCount": 2,
"precision": "ROOFTOP"
}
],
"summary": {
"partial": false,
"matchedCount": 1,
"rejectedCount": 0,
"truncated": false,
"requested": 1
},
"rejected": []
}Recommended use cases
- Preparing a batch of provider requests with correct pacing
- Turning two providers' answers into one comparable shape
- Reviewing exactly what would be sent before anyone sends it
Avoid for
- Actually calling a geocoding provider
- Anything requiring an API key to be supplied here
Related skills
Composable with
Safety & privacy
- Runs without calling any external service.
- Your payloads are never stored permanently.
- Telemetry captures metadata only — never your payload contents.
- We never train models on your payloads.
- No human reviews your payloads.