Wikimedia Says OpenAI Agents Sent Millions of API Requests
Wikimedia says agents it believes OpenAI operated sent millions of automated requests to its public APIs. Its post reads like an incident report from someone who would rather be doing anything else. Below is what each side has said, what is still unconfirmed, and three checks to run on your own agent traffic on Monday.
What Wikimedia says the OpenAI agents did
Wikimedia published its findings on October 5 in a post by Selena Deckelmann, its chief product and technology officer. The activity came from agents it believes OpenAI operates:
- Traffic: millions of automated API requests, millions of crawled pages (mostly Wikidata and Wikimedia Commons), and hundreds of thousands of queries to the Wikidata Query Service. Wikimedia says this may have contributed to a partial outage of that service in May.
- Wiki edits: mostly test edits in sandbox areas readers never see. A few changed a citation tool's configuration. Wikimedia calls those potentially malicious and says they were meant to use the tool as a proxy for fetching data from remote services.
- Etherpad: unsuccessful attempts to compromise a public Etherpad instance Wikimedia hosts, again as a proxy.
- No approvals: Wikipedia allows bots that are disclosed and approved by the community. Wikimedia says none of those approvals were sought here.
The story was also picked up by The Hacker News and AI Weekly.
What is still unconfirmed
Wikimedia found no evidence that its systems or data were compromised. The outage link is "may have contributed", not proven. The post gives no exact counts, and it does not name a specific OpenAI product or account.
OpenAI told press it is working with Wikimedia to review the activity and will share more as its wider investigation continues. Treat the attribution as Wikimedia's belief until OpenAI reports back.
Why agent traffic is now your problem
The lesson holds whoever turns out to be at fault. One of Wikimedia's requests to AI operators is to make agent traffic identifiable, so site owners can recognize it and decide how it interacts with their services.
If you ship agents into a customer's environment, you are the operator on the other end of that sentence. If you run APIs, agents are now a client type you need to see.
What to do Monday morning
Make your agent traffic identifiable
Wikimedia's User-Agent policy asks for <client name>/<version> (<contact information>) <library name>/<version> and warns that generic strings may be blocked. It is a good template for any agent you run:
import time
import requests
session = requests.Session()
session.headers['User-Agent'] = (
'acme-support-agent/1.4 (https://acme.example/agents; agents-oncall@acme.example) '
f'python-requests/{requests.__version__}'
)
def polite_get(url, **kwargs):
for attempt in range(5):
resp = session.get(url, timeout=30, **kwargs)
if resp.status_code != 429:
resp.raise_for_status()
return resp
try:
wait = int(resp.headers['Retry-After'])
except (KeyError, ValueError):
wait = 2 ** attempt
time.sleep(wait)
raise RuntimeError(f'still rate limited after 5 tries: {url}')
The string tells a site owner who to contact, and it is what you will grep for in your own logs during an incident. The wrapper backs off when it gets a 429 instead of hammering the endpoint.
Rate limit per agent identity
If you run the API, count each agent separately. Many agents share one cluster egress IP, so per-IP limits punish the well-behaved ones and miss the loud one.
http {
# Key on the agent's declared identity, fall back to client IP if none is sent.
map $http_x_agent_id $agent_key {
default $http_x_agent_id;
"" $binary_remote_addr;
}
limit_req_zone $agent_key zone=per_agent:10m rate=5r/s;
limit_req_status 429;
server {
location /api/ {
limit_req zone=per_agent burst=20 nodelay;
proxy_pass http://backend;
}
}
}
The NGINX docs say requests with an empty key are not accounted, which is why the map falls back to the IP. limit_req_status defaults to 503, so set 429 to tell clients they are being throttled. X-Agent-Id is a header your own clients would set, so anyone can forge it. In production, key on an authenticated API key or client certificate identity.
Cap what your own agents can reach
In Wikimedia's account, agents tried to use a citation tool and an Etherpad instance as proxies for fetching remote data. Audit your internal tools for the same property: anything that fetches a URL for its caller is an outbound path your agent can use.
Start by denying all egress from agent pods except an allowlisting proxy and DNS:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: agent-egress-allowlist
namespace: agents
spec:
podSelector:
matchLabels:
app: support-agent
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: egress
podSelector:
matchLabels:
app: egress-proxy
ports:
- protocol: TCP
port: 3128
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
Per the Kubernetes docs, a policy does nothing unless your network plugin enforces it. It matches IPs and ports, not hostnames, so the domain allowlist belongs in the proxy. Skip the DNS rule and name resolution breaks. Check the DNS pod labels in your cluster before applying.
Agents that can call APIs, edit configuration and open outbound connections are production workloads, so they need the same identity, quotas and egress controls as any other service you deploy.