Skip to content

Research custom attributes

Use custom research when you need an answer about a business that is not one of the standard enrichment attributes, for example whether it offers outdoor seating or which price band it falls in.

Define the attributes you want

Each attribute is a question you define yourself. Give it a name, a plain-language description of what you are asking, and a value_type of bool, number, string, or enum. For enum, list the permitted answers in allowed_values. The same attributes are researched for every place in the request. Send up to 20 attributes and up to 10 places.

Two names are reserved. owner_person returns the individual owner, co-owners, or franchisee, and owner_org returns the parent organisation when there is no individual owner. Request them like any other attribute.

Send a research request

Replace the sample names, addresses, and coordinates with the businesses you want to research. When providing coordinates, send both latitude and longitude in decimal degrees (WGS84).

curl https://api.voygr.tech/v1/deep-research \
  --request POST \
  --header "X-API-Key: $VOYGR_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "pois": [
      {
        "id": "bakery-001",
        "poi_name": "Example Bakery",
        "address": "123 Example Street",
        "latitude": 37.7749,
        "longitude": -122.4194,
        "country": "US"
      },
      {
        "id": "cafe-002",
        "poi_name": "Example Cafe",
        "address": "456 Sample Avenue",
        "latitude": 37.7755,
        "longitude": -122.4183,
        "country": "US"
      }
    ],
    "attributes": [
      {
        "name": "outdoor_seating",
        "description": "Does the place have outdoor seating?",
        "value_type": "bool"
      },
      {
        "name": "price_band",
        "description": "Typical price level",
        "value_type": "enum",
        "allowed_values": ["cheap", "mid", "expensive"]
      }
    ]
  }'

Control the cost before you commit

Research is billed at one credit per place per attribute, so two places with two attributes cost four credits. Attributes that could not be answered are still billed. Nothing is billed if research is unavailable.

To see the price first, send the same request with "estimate_only": true. Nothing runs and nothing is charged; the response returns meta.estimated_credits and an empty rows list. To cap a live request, set max_credits. If the estimate exceeds it, the request is rejected with 402 before any work starts and nothing is charged. To try a subset, set sample to the number of leading places to process; only those places run and are billed.

Interpret the research response

rows has one entry per place, in request order, with your id echoed back. Each row’s status is enriched when at least one attribute has a confirmed or estimated value, abstain when none does, cost_capped when the request’s research budget ran out, or error when that place failed. An error row carries an error message and null fields. It never fails the request, so the other rows still return.

If you sent verify_identity: true, each row also carries identity: for the name and the address, the value we settled on and researched against, a verdict (confirmed, corrected, unresolved) and the original you supplied. See the endpoint reference for how verification is priced.

fields maps each attribute name to its result. value is the answer, or null when it could not be established. status is confirmed, estimated (a derived value, always low confidence), unconfirmed, cost_capped, or rejected. confidence is high, medium, or low. Filter on status: "confirmed" when you want verified values only. A null value is a normal result, not an error, and it does not mean the answer is “no”.

meta.stopped_reason is completed when the request finished normally, cost_capped when the shared budget was exhausted, agent_unavailable when research was down, or estimate_only for a dry run (empty rows, nothing billed). When research is unavailable, every row abstains and nothing is billed. meta.estimated_credits is present on every response.

Handle research errors

A request with no places, more than 10 places, no attributes, or more than 20 attributes returns 400; read detail for the invalid fields. If an attribute asks for something the service does not research, the whole request returns 422 with error_code: FIELD_REJECTED and a rejected_fields object naming the attribute; remove or rephrase it and resend. Treat 401 and 403 as authentication and access errors; 403 means the key is not permitted to use research. A 402 means you do not have enough available credits or the estimate exceeded max_credits; waiting alone will not resolve it. For 429, wait and follow Retry-After when returned.