Skip to content
First-class clients

One API. Four SDKs. Every agent framework.

Typed clients in Python, Node, Go and Ruby, plus drop-in bindings for LangChain, LlamaIndex, Mastra and Agno. Same cache_ids, same sources[], same hard caps everywhere.

Languages

Official SDKs

pip install freshgeo
  • Fully typed with Pydantic v2 models
  • Sync and async clients
  • Automatic retry with exponential backoff on 5xx
  • Streaming replay for large snapshot exports
  • Built-in pytest fixtures for golden-set testing
  • Zero dependencies beyond httpx and pydantic
from freshgeo import FreshGeo

fg = FreshGeo(api_key="fg_live_...")

result = fg.pricing.compare(
    sku="DYSON-V15",
    sources=["currys.co.uk", "argos.co.uk", "ao.com"],
)

for offer in result.offers:
    print(f"{offer.source}: £{offer.price} — {offer.in_stock}")

print(f"cache_id: {result.cache_id}")
npm install @freshgeo/node
  • Full TypeScript types generated from our OpenAPI spec
  • Works in Node 20+, Bun, Deno and Cloudflare Workers
  • Tree-shakable — import only the APIs you use
  • Built-in Zod schemas for runtime validation
  • ESM and CJS builds
  • First-class Vercel AI SDK support
import { FreshGeo } from "@freshgeo/node";

const fg = new FreshGeo({ apiKey: process.env.FG_KEY! });

const intent = await fg.intent.lookup({
  domain: "monzo.com",
  topics: ["fraud detection", "compliance"],
});

console.log(`Score: ${intent.score}`);
console.log(`cache_id: ${intent.cache_id}`);
go get github.com/freshgeo/freshgeo-go
  • Context-aware — every call takes a context.Context
  • No reflection, no runtime code generation
  • Connection pooling tuned for high-throughput agents
  • Structured logging via slog
  • Go 1.22+ generics for typed responses
package main

import (
    "context"
    "fmt"
    "github.com/freshgeo/freshgeo-go"
)

func main() {
    fg := freshgeo.New("fg_live_...")
    jobs, err := fg.Jobs.Search(context.Background(), &freshgeo.JobsSearchParams{
        Domain: "revolut.com",
        Roles:  []string{"data engineer"},
    })
    if err != nil { panic(err) }
    fmt.Printf("Found %d jobs, cache_id=%s\n", len(jobs.Results), jobs.CacheID)
}
gem install freshgeo
  • Idiomatic Ruby — snake_case, Hash responses with dot access
  • Rails-friendly Railtie
  • ActiveSupport::Notifications for observability
  • Sidekiq-aware retry semantics
  • Ruby 3.1+
require "freshgeo"

fg = FreshGeo::Client.new(api_key: ENV.fetch("FG_KEY"))

listings = fg.real_estate.search(
  location: "Bristol BS8",
  beds_min: 2,
  budget_max_gbp: 450_000,
)

listings.results.each { |p| puts "#{p.address} — £#{p.price}" }
puts "cache_id: #{listings.cache_id}"
Framework bindings

Drop-in tool sets

LangChain

pip install langchain-freshgeo
from langchain_freshgeo import FreshGeoToolkit
from langchain.agents import AgentExecutor, create_openai_tools_agent
from langchain_openai import ChatOpenAI

toolkit = FreshGeoToolkit(api_key="fg_live_...", enabled=["pricing", "intent", "jobs"])
llm = ChatOpenAI(model="gpt-4.1")
agent = create_openai_tools_agent(llm, toolkit.get_tools(), prompt)
executor = AgentExecutor(agent=agent, tools=toolkit.get_tools())
executor.invoke({"input": "Should we lower pricing on the V15 this week?"})

LlamaIndex

pip install llama-index-tools-freshgeo
from llama_index.tools.freshgeo import FreshGeoToolSpec
from llama_index.agent.openai import OpenAIAgent

tools = FreshGeoToolSpec(api_key="fg_live_...", enabled=["news_risk", "competitor_monitoring"]).to_tool_list()
agent = OpenAIAgent.from_tools(tools, verbose=True)
print(agent.chat("Summarise this week's regulatory risk for UK challenger banks, with sources."))

Mastra

npm install @mastra/freshgeo
import { Mastra } from "@mastra/core";
import { freshgeoTools } from "@mastra/freshgeo";

const mastra = new Mastra({
  agents: {
    researcher: {
      model: "claude-sonnet-4-7",
      tools: freshgeoTools({
        apiKey: process.env.FG_KEY!,
        enabled: ["intent", "jobs", "social"],
        dailyCapGbp: 10,
      }),
    },
  },
});
await mastra.agents.researcher.generate("Build an account brief for gocardless.com");

Agno

pip install agno-freshgeo
from agno.agent import Agent
from agno.models.anthropic import Claude
from agno_freshgeo import FreshGeoTools

agent = Agent(
    model=Claude(id="claude-sonnet-4-7"),
    tools=[FreshGeoTools(api_key="fg_live_...", enabled=["real_estate", "news_risk"])],
    instructions="Always cite cache_id and sources[] in your final answer.",
)
agent.print_response("Find 2-bed flats in Bristol under £400k with no recent flood-risk news.")
FAQ

SDK FAQ

Are the SDKs open source?+

Yes, all four are MIT-licensed on GitHub. The framework bindings live in separate repos so you can fork just what you use.

Do SDKs add overhead over raw HTTP?+

Under 3ms median on Python and Node, measured against curl. Connection pooling saves more than the serialisation costs on any agent doing more than a handful of calls.

What about Rust, Java, .NET?+

Community SDKs exist for Rust and Java; we sponsor them but do not own them. A first-party .NET SDK is on the roadmap for Q4.

Can I pin an SDK version to a parser version?+

Yes — pass parser_pins at client construction, or set them per-call. Pins are enforced server-side, so every language behaves identically.

How do framework bindings handle cache_ids?+

Every tool call returns the cache_id in the tool response message, so your agent's transcript naturally carries the audit trail. No extra wiring.

Is MCP the only way to use FreshGeo from Claude?+

No. MCP is the most convenient, but you can also use the Node or Python SDK as a plain tool. MCP gets you the hard caps and key scoping for free.