Blog / API guides

Google Events API: Find Local Events as JSON

Send a GET to /v1/google-events/search with a query such as concerts in austin, and you get Google's event listings back as JSON: title, date, address, venue, ticket links and a thumbnail. The call costs 1 credit, and a failed call costs nothing. This guide covers the parameters, a recorded response, an honest look at the date and location filters, and a small script that builds a city events feed.

What does the Google Events API return?

The endpoint returns the event cards Google shows for a search such as "events in london this weekend", as structured JSON. Each item in events_results has a position, title, a date object, an address array, a link, a venue object, ticket_info entries and a thumbnail. The response also echoes your inputs in search_parameters and includes a pagination object.

That makes it a fit for city guides, "what's on" widgets, newsletters and agents that answer "what can I do this weekend". It isn't a catalogue of every event in a city. You get what Google ranks for the query you send, and the data is collected from publicly available sources and returned as found at the time of the call.

If you need ticketing, inventory or on-sale data from a single organiser, that organiser's own API usually fits better. Ticketmaster, for example, publishes its own Discovery API for its events. For breadth across venues and organisers in one query, a search-based endpoint is the more practical route.

How do you make your first request?

Send the key in the X-API-Key header and put the search in q. Keys start with sk_live_, and you get one free by signing up and verifying an email address. The authentication docs cover the api_key query parameter for places where headers aren't possible.

curl "https://api.perceptlayer.com/v1/google-events/search?q=concerts+in+austin&hl=en&gl=us" \
  -H "X-API-Key: $API_KEY"

Every successful response carries X-Credits-Used and X-Credits-Remaining headers, so you can check what the call cost without parsing the body. You can also run the same call from the playground on the Google Events search endpoint page before writing any code.

Which parameters does the search endpoint take?

The endpoint takes one required parameter, q, and six optional ones. Types and length limits are the only documented constraints, so the table sticks to those and adds notes on how each one is meant to be used.

Parameter Type Required Limit Notes
q string yes 255 characters The search text, such as events in london this weekend.
hl string no none documented Interface language, for example en.
gl string no 2 characters Country code, for example us.
location string no 255 characters A place name to anchor the search.
uule string no 500 characters Google's encoded location value, for when you already have one.
htichips string no 200 characters A filter chip value. Allowed values aren't documented here.
start integer no none documented Result offset, for paging.

Defaults and page sizes aren't documented, so don't hard-code either. Read events_results and pagination in the response instead.

Invalid input returns invalid_parameters (422), for example a q over 255 characters. The errors docs list every code, and since a failed call costs nothing, a bad request is only a lost minute.

What does a real response look like?

Here is a recorded response for q=events in london this weekend with hl=en and gl=uk. The excerpt is trimmed with … and shows the first event only.

curl "https://api.perceptlayer.com/v1/google-events/search?q=events+in+london+this+weekend&hl=en&gl=uk" \
  -H "X-API-Key: $API_KEY"
{
  "search_parameters": {
    "engine": "google_events",
    "q": "events in london this weekend",
    "gl": "uk",
    "hl": "en",
    "start": 0
  },
  "search_information": {
    "events_results_state": "Results for exact spelling"
  },
  "events_results": [
    {
      "position": 1,
      "title": "Halloween River Cruise",
      "date": {
        "when": "Sat, 24 Oct 2026, 19:00",
        "start_time": "2026-10-24T18:00:00Z",
        "end_time": "2026-10-24T19:00:00Z"
      },
      "address": [
        "Riverside Building, London Eye, County Hall, Westminster Bridge Rd, London SE1 7PB"
      ],
      "description": "A 60-minute London Eye River Cruise along the Thames with live actors telling ghost stories of Jack the Ripper, the ghost of Captain Kidd and the haunted cries …",
      "ticket_info": [
        {
          "source": "Mato Events",
          "link": "https://ma.to/event/london-eye-halloween-river-cruise-2026-10-24",
          "link_type": "tickets"
        },
        {
          "source": "londoneye.com",
          "link": "https://www.londoneye.com/",
          "link_type": "more info"
        }
      ],
      "venue": {
        "name": "London Eye",
        "rating": 4.5,
        "reviews": 206031,
        "link": "https://www.google.com/search?q=London+Eye&ludocid=4796882358840715922"
      },
      "thumbnail": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSIyGJkdedlIlKt-RDN0ud-FP177CCvK_cM-VDPtGilIsEngLzXTMXfTGmHpgIvc6S0f3hXZaJaUcgcI3w"
    }
  ],
  "pagination": {
    "current_page": 1,
    "start": 0
  }
}

A few details in the recording matter when you build on it:

  • date.when is display text in local time ("Sat, 24 Oct 2026, 19:00"), while start_time is UTC. The 19:00 event has a start_time of 18:00:00Z because London was on summer time that day.
  • The second event in the recording, a ghost walk, has when set to "Sun, 25 Oct 2026" with no clock time. Its start_time and end_time are both 2026-10-24T23:00:00Z. Date-only events can have identical start and end times, so don't treat the pair as a duration.
  • description is cut off with an ellipsis and the same event shows ? where the source text had other characters. Show it as a teaser, and link to the event instead of republishing it.
  • ticket_info mixes tickets and more info links, distinguished by link_type.

How reliable are the date and location filters?

There is no date or from/to parameter. The timeframe lives in the wording of q ("tonight", "this weekend", "in november") and possibly in htichips, whose accepted values aren't documented here. Both steer Google's ranking. Neither guarantees every result falls inside your window, so filter again on your side.

The recorded call above is a fair example: it asked for "this weekend" and the events it holds fall on 24 and 25 October 2026. That is what you'd hope for, but it comes from Google's interpretation of the phrase, and "weekend" depends on the day you run the query.

Location works the same way:

  • In q: "concerts in austin" carries the city in the text, as in the first example.
  • location: a free-text place name up to 255 characters, for when you'd rather keep the city out of the query.
  • uule: Google's encoded location string, useful if you already generate them.
  • gl: a two-character country bias. The recorded call used gl=uk, and it returned London events. Google's own code for the United Kingdom is usually gb, and we haven't documented the accepted gl values, so try gb if uk doesn't bias results the way you expect.

Compare address against the city you asked for and drop mismatches. Run a few queries in the playground before choosing between location and a city in q, because we haven't documented which wins when they disagree.

How do you build a city events feed?

A city feed is a handful of query phrasings per city, a page or two of each, merged and deduplicated, then filtered to a time window. The script below does that with requests. It pages by advancing start by the number of events it just received, which assumes start is a plain offset. Confirm that in the playground before relying on it.

import os
import time
from datetime import datetime, timedelta, timezone
from zoneinfo import ZoneInfo

import requests

URL = "https://api.perceptlayer.com/v1/google-events/search"
HEADERS = {"X-API-Key": os.environ["API_KEY"]}


def fetch(q, gl, start=0):
    for _ in range(3):
        r = requests.get(
            URL,
            params={"q": q, "hl": "en", "gl": gl, "start": start},
            headers=HEADERS,
            timeout=30,
        )
        if r.status_code in (429, 503):
            time.sleep(int(r.headers.get("Retry-After", "5")))
            continue
        r.raise_for_status()
        return r.json()
    raise RuntimeError(f"gave up on {q!r}")


def city_feed(city, gl, tz, days=7, pages=2):
    phrasings = [f"events in {city} this weekend", f"concerts in {city}", f"things to do in {city}"]
    now = datetime.now(timezone.utc)
    cutoff = now + timedelta(days=days)
    seen = {}
    for q in phrasings:
        start = 0
        for _ in range(pages):
            events = fetch(q, gl, start).get("events_results", [])
            if not events:
                break
            for e in events:
                when = e.get("date", {}).get("start_time")
                if not when:
                    continue
                begins = datetime.fromisoformat(when.replace("Z", "+00:00"))
                if not now <= begins <= cutoff:
                    continue
                key = (e["title"].lower(), when, e.get("venue", {}).get("name"))
                seen.setdefault(key, {
                    "title": e["title"],
                    "starts_local": begins.astimezone(ZoneInfo(tz)).isoformat(),
                    "venue": e.get("venue", {}).get("name"),
                    "address": ", ".join(e.get("address", [])),
                    "tickets": [t["link"] for t in e.get("ticket_info", []) if t.get("link_type") == "tickets"],
                })
            start += len(events)
    return sorted(seen.values(), key=lambda e: e["starts_local"])


if __name__ == "__main__":
    for event in city_feed("london", "uk", "Europe/London"):
        print(event["starts_local"], event["title"], "-", event["venue"])

Three details in that script come straight from the recorded response. It filters on start_time because that field is machine-readable UTC. It converts to the city's time zone for display. And it deduplicates on title, start time and venue, because the same event can surface under more than one query. The same idea at larger scale is in deduplicating listings across sources.

The script retries 429 and 503 after the Retry-After seconds. One caution: requests have no idempotency key, so a call that timed out on your side may still have succeeded and been billed. The rate limits docs cover the per-minute counts, and free accounts get 1 request a second.

What does an events feed cost per refresh?

Every successful call costs 1 credit, so a refresh costs queries × pages × cities, in credits. Failed calls cost nothing and return X-Credits-Used: 0. The script above runs 3 phrasings with up to 2 pages each, so one city costs at most 6 credits per refresh.

Setup Calls Credits
1 city, 3 phrasings, 2 pages 6 6
50 cities, 3 phrasings, 1 page 150 150
50 cities, 3 phrasings, 2 pages, daily for 30 days 9,000 9,000

The last row is 50 × 3 × 2 = 300 calls a day, and 300 calls × 30 days × 1 credit = 9,000 credits. That fits inside the Starter pack's 10,000 credits, and purchased credits last 12 months. The monthly free tier of 1,000 credits covers about three days of that feed, or a full test of a few cities. See pricing for what credits cost and how credits are counted for the details.

Cutting cost is mostly a question of calls you don't make. Most event lists don't change by the hour, so a daily or twice-daily refresh is enough for a guide or a newsletter. If you need to schedule the pulls, cron, queues or workflows compares the options.

When should you use this instead of another source?

Use the endpoint when you want what people see in a Google events search, across many venues, without scraping it yourself. Use your own pipeline, or an organiser's API, when you need complete inventory for one venue or one ticketing platform. The endpoint returns ranked search results, not a database of everything on sale.

If you publish events on your own site, Google documents how to mark them up in its event structured data guide. That is the reverse direction: it helps your events appear in Google, while this endpoint reads what Google shows. Both are useful if you run a venue and also want to track what's listed around you.

For agents, the same endpoint is available through the MCP server, so an assistant can answer "what's on in Lisbon on Saturday" with a live call. The AI agents docs show how to add it. For related Google endpoints, Google Hotels pairs naturally with an events feed for trip planning, and the Google Places alternative covers venue data.

Your next step: get a free API key, then run concerts in austin from the playground on the Google Events search page and compare the address fields with the city you asked for.

APIs in this post

Frequently asked questions

How much does a Google Events API call cost?

A successful call to /v1/google-events/search costs 1 credit. Failed calls cost nothing and return X-Credits-Used: 0. Free accounts get 1,000 credits a month after verifying an email address, with no credit card. That's 1,000 calls a month before you buy anything. The pricing page shows what credit packs cost.

Can I filter Google events by date?

There's no date parameter. Put the timeframe in q, such as "this weekend" or "tonight", and then filter the results on date.start_time, which is in UTC. htichips also accepts a filter string, but its allowed values aren't documented, so test any value in the playground first.

How do I get events for a specific city?

Include the city in q, as in concerts in austin, or pass a place name in location. You can also add gl for the country. Then check each result's address array against your city, because these inputs steer the ranking and don't guarantee that every result matches.

What happens when I hit the rate limit?

You get a 429 response with a Retry-After header giving the seconds to wait. Free accounts get 1 request a second, and paid accounts run at the rate of the credits they're using, at least 5 a second. Wait the stated time and retry. The rate limits docs have the details.

Is there a way to call it from an AI agent?

Yes. The MCP server at https://api.perceptlayer.com/mcp exposes search_endpoints, get_endpoint and call_endpoint. The first two only look up the catalogue and cost nothing. call_endpoint runs the Google Events search and spends 1 credit on success. See the AI agents docs.

  1. Google News API: Search News Articles as JSON

    One GET call to /v1/google-news/news returns articles with title, link, source and ISO date as JSON for 1 credit. Parameters, a real response, cost.

  2. Google Patents API: Search Patents and Get Details

    Search Google Patents by keyword, inventor or assignee with one GET call, then fetch full details by patent_id. Both endpoints cost 1 credit per call.

  3. Yelp Reviews API: Business Search and Reviews as JSON

    Three Yelp endpoints return search results, business details and reviews as JSON, 1 credit per call. Parameters, a real response and a cost example.

Start with 1,000 free credits

No password to set. We email you a sign-in link, and your key works on every API.

No credit card. 1,000 free credits a month.