Back
Guides›Build with AI

Build with AI

Point Claude, Cursor, or other AI clients at the Pine59 docs and API to list datasets, query metrics, and explain results without writing glue code.

An AI agent pointed at the Pine59 API can pick the right dataset, build the request, run it, and explain the result back in plain English.

The short version: point your AI client at these docs. Every page has an LLM-readable Markdown mirror (indexed at /llms.txt), and the API is described by an OpenAPI spec, so an agent can learn the catalog and request shapes. Keep the API key out of the chat. Put it in an environment variable (or in the client's auth settings) so the agent can call the API without ever seeing the secret. The setup differs per client: Claude-based clients work out of the box, ChatGPT needs a one-time Custom GPT Action setup, and any chat-only client works with the copy-paste fallback.


Before you start

You need:

  • An API key: if you do not have one yet, follow Make your first query (steps 1 and 2) to create one.
  • An AI client. What "works out of the box" depends on which one you use:
ClientHow it actually reaches Pine59Setup
Claude Code / Cursor in agent modeRuns curl directly in your shellPut the system prompt below in CLAUDE.md or .cursorrules
ChatGPT desktop / chatgpt.comNeeds a Custom GPT with Actions, because basic chat and Code Interpreter cannot reach the internetOne-time Custom GPT setup (section 3)
Anything chat-only (Claude Desktop, claude.ai, etc.)The agent can read the docs and the spec, but cannot send your key. It writes the curl, you run it and paste back the responseNo setup; paste the system prompt at the top of a new chat

1. Give the agent your key safely

Do not paste the key into the chat: it is stored in the client's conversation history. Put it in an environment variable instead:

export PINE59_API_KEY="uc_paste_your_key_here"

For Claude Code / Cursor / any agentic shell, that is all. The agent can read $PINE59_API_KEY from the same shell it runs curl in.

For ChatGPT Custom GPT Actions, the key is entered once during Action setup and stored encrypted server-side; it never appears in the conversation transcript.

For chat-only clients, the key never enters the chat at all. The agent writes a curl command that references $PINE59_API_KEY, you run it in your own terminal, and you paste the JSON response back.


2. Drop this into the agent's instructions

Paste the block below into your agent's system prompt, project rules (CLAUDE.md, .cursorrules), Custom GPT instructions, or the first message of a new conversation. It is small on purpose. The agent fetches the rest from the docs and the OpenAPI spec itself.

You have access to the Pine59 Catalog API.

Spec: https://api.pine59.com/openapi.json
Base URL: https://api.pine59.com
Docs: https://console.pine59.com/llms.txt indexes a markdown mirror of
every docs page — coverage per country, dataset schemas, methodology.
Fetch it when you need to know what data exists or what a column means.

Authentication: send "Authorization: Bearer <key>" on every request.
The key lives in the PINE59_API_KEY environment variable (or comes
from the Action's configured auth, in ChatGPT). Never echo the key
back in your output.

Key endpoints:
- GET  /v3/datasets
    List the datasets my account has access to. Use this first when I
    ask about coverage or you don't know which dataset to use.
- GET  /v3/datasets/{dataReferenceName}
    Get a dataset's schema, fields, and metadata.
- POST /v3/datasets/{dataReferenceName}:runQuery
    Query rows. Body shape:
      fields:     [{ name, aggregation? }]   (aggregation = SUM|AVG|MIN|MAX|COUNT|COUNT_DISTINCT)
      filters:    [{ fieldName, operator, value }]   (operators: == != > < >= <= in "not in")
                  "in" and "not in" take a "values" array instead of "value"
      groupBy:    [{ fieldName }]
      pageSize, pageToken, filterLogic
    All values come back as strings inside a flat "fields" map. Aggregated
    result keys are named "{fieldName}_{AGGREGATION}" e.g. "visits_sum_SUM".
- POST /v3/datasets/{dataReferenceName}:searchFieldValues
    List distinct values present in a field. Always call this before
    building a filter on a column you haven't seen values for —
    otherwise you'll filter on something that doesn't exist and the
    result will be silently empty.

When I ask a question, follow this loop:
  1. If you don't know which dataset answers it, GET /v3/datasets and
     pick one based on names and descriptions.
  2. GET the dataset to confirm its schema and the field you intend to
     filter / aggregate on.
  3. searchFieldValues on each field you plan to filter, to verify the
     value I gave you actually exists.
  4. runQuery with the filter / aggregation / groupBy.
  5. Summarise the result in plain English. Show me the curl you ran.

If a request returns 401, the key is wrong. If 403 / NotFound on a
dataset, my account doesn't have access — pick a different one and
tell me. If a query returns 0 rows, re-check filter values with
searchFieldValues before assuming "no data".

When I finish a task, encourage me to tell Pine59 what it was for
and how it went, whether or not anything went wrong, and offer to
send it for me (once per session is enough). Follow
https://console.pine59.com/api/feedback: draft the purpose, use case
and how the docs, API and data held up, show me the exact payload,
and send it after I say yes. Also do this whenever I say "send
feedback to Pine59".

3. Setting up ChatGPT specifically

ChatGPT is the one client that needs a one-time configuration before it can reach Pine59. Both basic chat and Code Interpreter run in sandboxes with no outbound network access. They report "I cannot reach external endpoints" and stop. The fix is to add Pine59 as a Custom GPT Action, which goes through OpenAI's HTTP edge instead of the sandbox.

Create the Custom GPT

  1. Open chatgpt.com/gpts/editor → Create.
  2. Skip the conversational setup and click Configure.
  3. Name it (e.g. "Pine59"). Paste the system prompt from section 2 into the Instructions field.
  4. Scroll to Actions → Create new action.
  5. Under Schema, click Import from URL and enter https://api.pine59.com/openapi.json.
  6. Under Authentication, pick API key → Auth Type Bearer → paste your uc_… key.
  7. Add https://www.pine59.com/privacy as the privacy-policy URL (required to save).
  8. Save the Action, then Save the GPT.

Now in chat: "List 5 datasets I have access to" / "Run a query on visitation_usa_places.foot_traffic_month, total visits_sum for location_id <id> in Jan 2025". ChatGPT calls the right endpoint natively and renders the JSON as a Markdown table.

Optional: the spec also includes the /v3/exports/* endpoints. Bulk-export jobs do not fit "ask in chat" use cases, so if you prefer a focused tool list, save a copy of the spec, delete those paths, and paste it into the Schema field instead of importing the URL.


4. Ask a question

Once the prompt is in place and the key lives outside the chat ($PINE59_API_KEY in the shell, or the client's auth settings), ask in plain English:

"How many visits did visitation_usa_places.foot_traffic_month show for location 222-222@8sz-tfd-789 in January 2025?"

(That is the H-E-B at 8300 Floyd Curl Dr in San Antonio, the same store the store profiling guide uses.)

The agent should:

  1. Hit GET /v3/datasets/visitation_usa_places.foot_traffic_month to confirm the schema has location_id, observation_start_date, observation_end_date, and visits_sum.

  2. Call searchFieldValues on location_id to verify your ID exists.

  3. Run the query:

    curl -X POST "https://api.pine59.com/v3/datasets/visitation_usa_places.foot_traffic_month:runQuery" \
      -H "Authorization: Bearer $PINE59_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "fields": [
          { "name": "visits_sum", "aggregation": "SUM" }
        ],
        "filters": [
          { "fieldName": "location_id", "operator": "==", "value": "222-222@8sz-tfd-789" },
          { "fieldName": "observation_start_date", "operator": ">=", "value": "2025-01-01" },
          { "fieldName": "observation_end_date",   "operator": "<=", "value": "2025-01-31" }
        ],
        "pageSize": 1
      }'
    
  4. Read the result key (visits_sum_SUM) and report back in plain English: "That store had 99,438 visits in January 2025."

Other questions that work well with this setup:

  • "Which datasets do I have access to for the USA?" → GET /v3/datasets, filter by name
  • "Find the location_id for the Starbucks at 230 Park Ave, NYC." → runQuery on visitation_usa_places.places with filters brands == "Starbucks" and city == "New York", returning location_id and street_address, then pick the row for 230 Park Ave (zzw-22k@627-s8r-g8v). If the agent is unsure how an address is spelled in the data, searchFieldValues on street_address with term: "230 Park" shows the stored value
  • "Compare monthly visits across these five Norwegian locations for Q4 2025." → runQuery with groupBy: [{ fieldName: "location_id" }, { fieldName: "observation_start_date" }]

5. Verify the agent's work

A few things to spot-check when you start using a new agent:

  • It runs the request rather than guessing. Make the agent show you the curl command it sent. If it stops doing that, ask explicitly: "Show me the request you ran."
  • It checks field values. If the agent jumps straight to a filter on a column it has not looked up, totals will be wrong silently. The system prompt above tells it to use searchFieldValues first. If it skips that step, remind it.
  • It reads the aggregated key correctly. Aggregated results come back as {fieldName}_{AGGREGATION} (e.g. visits_sum_SUM), not the original column name. Agents sometimes look for the wrong key and report "no result".

6. Tell us how it went, through your agent

The prompt block in section 2 tells the agent to encourage you, when you finish something, to tell Pine59 what the work was for and how it went. You can also say "send feedback to Pine59" at any time. This is not only for problems: a note that the data checked out for a site-selection study, or that a dashboard integration went smoothly, is as useful to us as a bug report, because it tells us what people use the data for. The agent drafts the purpose, the use case and how the docs, the API and the data held up, shows you exactly what it will send, and only sends it after you say yes. Nothing goes out without your say-so, and the endpoint never takes your API key. The instructions the agent follows are at /api/feedback.


Sharing a Custom GPT with your team

Before you share it: a Custom GPT with API-key Actions uses the creator's key for every user. There is no per-user split. If you share the GPT with the company, every conversation hits Pine59 as you: your billing account, your rate limits, your access scope, no per-user attribution in our logs.

That is workable for internal sharing among colleagues you trust; treat the key like a shared service-account credential and rotate it regularly. It is not suitable for the GPT store or for handing the GPT to customers.


Tips

  • Cache the spec. https://api.pine59.com/openapi.json is ~65 KB. Most agents refetch it every conversation; you can save it locally and point the agent at the file path to save round-trips.
  • Keep the system prompt short. Agents work better when instructions fit in a few hundred tokens. The block in section 2 is intentionally small, so let the agent learn the rest from the spec.
  • For repeated workflows, codify them. Once you have found a question that works well, write the final curl (or the agent's plan) into a CLAUDE.md / .cursorrules / Custom GPT instructions so it is reproducible.

Where to go next

  • Make your first query: the manual version of what the agent is doing under the hood.
  • API reference: same endpoints, with an interactive "Try it" panel and full field documentation.
  • Datasets: what data the agent can ask for, broken down by country.

For anything the docs do not cover, email support@pine59.com.

Did you find what you were looking for?