---
name: gdelt-ingestion
description: "How to get GDELT 2.0 (Global news events and tone) data into a pandas DataFrame and Parquet: access, the request, the format, limits, quirks and terms."
---

# Load GDELT 2.0 event files into pandas and Parquet

Source entry: https://signal-lab-jet.vercel.app/datasets/gdelt
Provider: https://www.gdeltproject.org/data.html
Checked against the provider's documentation: 2026-10-29

## Access
- No account or key. Raw files are plain downloads.

## Request
```bash
curl https://data.gdeltproject.org/gdeltv2/lastupdate.txt
```

## Format
Every 15 minutes: zipped export (events), mentions and gkg files. Despite the .CSV name they are tab-delimited, with no header row; the events file has 61 columns in codebook order.

## Load into pandas and Parquet
Needs Python 3 with requests, pandas and pyarrow.
```python
"""GDELT 2.0 Events: the latest 15-minute export file -> pandas -> Parquet. No key."""
import io
import zipfile
import requests
import pandas as pd

# lastupdate.txt lists the newest export, mentions and gkg files as "size md5 url".
lines = requests.get("https://data.gdeltproject.org/gdeltv2/lastupdate.txt", timeout=30).text.split("\n")
url = next(l.split()[2] for l in lines if l.endswith(".export.CSV.zip"))
url = url.replace("http://", "https://")  # the list gives http:// links; https:// also serves them
stamp = pd.Timestamp(url.rsplit("/", 1)[1][:14])
for _ in range(5):  # the newest file can 404 for a while; step back one 15-minute slot at a time
    r = requests.get(url, timeout=60)
    if r.status_code != 404:
        break
    stamp -= pd.Timedelta(minutes=15)
    url = f"https://data.gdeltproject.org/gdeltv2/{stamp:%Y%m%d%H%M%S}.export.CSV.zip"
r.raise_for_status()
with zipfile.ZipFile(io.BytesIO(r.content)) as z:
    raw = z.read(z.namelist()[0])

# Tab-delimited despite the .CSV name, no header row, 61 columns (GDELT 2.0 Event codebook).
df = pd.read_csv(io.BytesIO(raw), sep="\t", header=None, dtype=str)
cols = {0: "GlobalEventID", 1: "Day", 6: "Actor1Name", 16: "Actor2Name", 26: "EventCode",
        28: "EventRootCode", 29: "QuadClass", 30: "GoldsteinScale", 31: "NumMentions",
        34: "AvgTone", 53: "ActionGeo_CountryCode", 59: "DATEADDED", 60: "SOURCEURL"}
df = df[list(cols)].rename(columns=cols)          # keep EventCode as a string (leading zeros matter)
for c in ("GoldsteinScale", "AvgTone", "NumMentions", "QuadClass"):
    df[c] = pd.to_numeric(df[c])
df["Day"] = pd.to_datetime(df["Day"], format="%Y%m%d")
df["DATEADDED"] = pd.to_datetime(df["DATEADDED"], format="%Y%m%d%H%M%S")
df.to_parquet("gdelt_events_latest.parquet", index=False)
print(url.rsplit("/", 1)[1], len(df), "events; oldest event Day", df["Day"].min().date())
```
Run by us on 2026-10-29 (python 3.13.5 | pandas 3.1.0 | pyarrow 26.0.0 | requests 2.34.2). Result: 841 events from the 2026-10-29 23:30 UTC export file.

## Limits
- Raw files: no published rate limit.
- DOC 2.0 search API: one request every 5 seconds (GDELT's own limit message).

## Quirks
- Store EventCode (and the base and root codes) as strings: CAMEO codes can have leading zeros.
- Day is the date the event took place; DATEADDED is when GDELT added it. Use DATEADDED for "what was new" (rows in the file we checked had Day values up to a year earlier).
- Observed 2026-10-29: lastupdate.txt lists http:// links, which returned HTTP 500 from our test environment while the same paths over https:// worked, and the newest listed file returned 404 for some minutes after it was listed. The snippet switches to https and steps back one 15-minute slot.

## Terms and attribution
- Free for any use, including commercial; any use or redistribution must cite the GDELT Project and link to https://www.gdeltproject.org/.
- Terms page: https://www.gdeltproject.org/about.html

## Sources
- https://data.gdeltproject.org/documentation/GDELT-Event_Codebook-V2.0.pdf: "are tab-delimited (note that files have a “.csv” extension, but are actually tab-delimited)"
- https://data.gdeltproject.org/documentation/GDELT-Event_Codebook-V2.0.pdf: "it is strongly recommended that this field be stored as a string instead of an integer"
- https://www.gdeltproject.org/about.html: "any use or redistribution of the data must include a citation to the GDELT Project and a link to this website"

Information for educational purposes only. Free tiers, limits and licenses belong to the providers and may change; check the provider's own terms before using the data.
