Repository navigation
Errors and troubleshooting
This page describes what happens when a request to Event Registry fails, how the package repeats the failed requests, how to check your token usage, how to see what the package is doing, and the most common mistakes.
The typed exceptions, the retry behavior and the raiseOnError parameter described here are available since version 11.0 of the package. If you are updating from an older version, see the migration guide.
When a request fails, the methods that make requests (EventRegistry.execQuery(), jsonRequest(), the suggest*() and get*Uri() methods, the iterators, TopicPage.getArticles(), the Analytics methods, ...) raise an exception. All the exceptions derive from EventRegistryError, which derives from Exception:
EventRegistryError
+-- NoDataError 204: the request succeeded, but the requested information is not available
+-- InvalidRequestError 400 and the other 4xx codes: invalid or missing parameters, unknown endpoint, ...
+-- AccessError
| +-- UnauthorizedError 401: the API key is not valid, or you reached the limit of the tokens in your account
| +-- ForbiddenError 403: your IP or account is disabled, or all the tokens have been used
+-- RateLimitError 429: too many simultaneous requests (at most 5 are allowed)
+-- ServerError 5xx: internal error or the service is temporarily unavailable
+-- InvalidResponseError the server returned a response that is not valid json
+-- QueryError the server processed the request, but returned an error instead of the results
+-- NetworkError the request could not be made (connection problem) or it timed out
| Exception | When is it raised | Repeated automatically |
|---|---|---|
NoDataError |
status 204: the request succeeded, but the requested information is not available | no |
InvalidRequestError |
status 400 and other 4xx codes: invalid or missing parameters | no |
UnauthorizedError |
status 401: the API key is not valid, or you reached the limit of the tokens in your account | no |
ForbiddenError |
status 403: your IP or account is disabled, or all the tokens have been used | no |
RateLimitError |
status 429: too many simultaneous requests (at most 5 are allowed). Not raised by the package - such requests are repeated until they succeed | yes |
ServerError |
status 5xx: internal error or the service is temporarily unavailable | yes |
InvalidResponseError |
the server returned a response that is not valid json (for example a truncated response) | yes |
NetworkError |
the request could not be made or it timed out (a request times out after 60 seconds) | yes |
QueryError |
the server processed the request (status 200), but returned an error message instead of the results | no |
Each exception has the following attributes:
-
statusCode: the HTTP status code of the response (Noneif there was no response, as withNetworkError) -
responseText: the text returned by the server - it usually explains the error -
url: the URL of the request -
retryAfter: the number of seconds after which the request can be repeated, if the server said so (theRetry-Afterheader), otherwiseNone -
isTemporary: True for the errors where repeating the same request later can succeed (RateLimitError,ServerError,InvalidResponseError,NetworkError)
For a NetworkError, the original exception of the requests library is available in the __cause__ attribute.
You can catch all the errors at once, or catch the specific ones that you want to handle differently (list the more specific classes first):
from eventregistry import *
er = EventRegistry(apiKey = YOUR_API_KEY)
try:
res = er.execQuery(QueryArticles(keywords = "Tesla"))
except AccessError as ex:
# UnauthorizedError or ForbiddenError
print("Check your API key and tokens:", ex.responseText)
except NetworkError as ex:
print("Connection problem:", ex)
except EventRegistryError as ex:
print("The request failed with status %s: %s" % (ex.statusCode, ex))Since all the exceptions derive from Exception, code that catches Exception keeps working.
Invalid arguments are detected before a request is made and raise the standard TypeError (wrong type, unknown argument) or ValueError (invalid value, e.g. count above the limit) - these are programming errors and are not EventRegistryError.
401 - UnauthorizedError. The API key is not recognized, or you have reached the limit of the tokens in your account. Check that the key is the one shown on your dashboard and that it is really the one being used (the apiKey argument has priority over the EVENTREGISTRY_API_KEY environment variable, which has priority over the settings.json file). Then check the token usage on the dashboard or with er.getUsageInfo() (see below). The request is not repeated, since the result would be the same.
403 - ForbiddenError. Your IP or account is disabled, or all the tokens have been used. The responseText attribute of the exception contains the explanation. If you have used all the tokens, check the usage on your dashboard and the available plans. The request is not repeated.
429 - RateLimitError. You are making too many requests at the same time. At most 5 requests of a user can be executed simultaneously; the requests above this limit are rejected. This is not related to the number of tokens. The package logs a warning, waits and repeats the request until it succeeds (see Retries), so the RateLimitError is not raised and an iteration over the results is not interrupted. The warnings are the sign that the requests are slowed down - reduce the number of requests that you make at the same time.
A user (API key) can have at most 5 requests in progress at the same time. How to stay under the limit:
- Make the requests sequentially, one after another. A single
EventRegistryinstance already does this: it makes one request at a time, also when it is used from several threads. A script that uses one instance therefore never has more than one request in progress. - Count all the places where the same API key is used at the same time: several processes, several machines, scheduled jobs that overlap, notebooks left running. If you run several workers in parallel, use at most 5 of them (fewer, if the key is also used elsewhere).
- Use the
minDelayBetweenRequestsparameter of theEventRegistryconstructor (0.5 seconds by default) to set the minimum time between two requests of an instance. Increasing it slows the workers down and makes overlaps less likely.
from eventregistry import *
# one instance, shared by the whole script; at least 1 second between two requests
er = EventRegistry(apiKey = YOUR_API_KEY, minDelayBetweenRequests = 1.0)
for keyword in ["Tesla", "Rivian", "Lucid Motors"]:
q = QueryArticlesIter(keywords = keyword, lang = "eng")
for art in q.execQuery(er, sortBy = "date", maxItems = 100):
print(art["title"])A request that fails with a temporary error - a connection problem, a timeout, a 5xx status or a response that is not valid json - is automatically repeated. Other errors (invalid request, invalid API key, no more tokens, ...) are raised immediately, since repeating the request would give the same result.
- The number of repeats is set with the
repeatFailedRequestCountparameter of theEventRegistryconstructor. The default is 3, which means that a request is made at most 4 times (the first attempt and 3 repeats) before the exception is raised. - Use
repeatFailedRequestCount = -1to repeat the request indefinitely (the default before version 11.0). The call then does not return until the request succeeds. - Use
repeatFailedRequestCount = 0to never repeat a request. - A request rejected with status 429 (too many simultaneous requests) is always repeated, until it succeeds, regardless of
repeatFailedRequestCount. It will succeed once the other requests finish, while raising an error would end an iteration over the results, which could then not be resumed. These repeats are not counted towardsrepeatFailedRequestCount. - The delay before a repeat grows exponentially: 2, 4, 8, 16, ... seconds, but at most 60 seconds. If the server returns the
Retry-Afterheader, that delay is used instead (also at most 60 seconds). - While a failed request waits to be repeated, the requests of other threads that use the same instance are not blocked.
from eventregistry import *
# a long-running job that should survive longer outages: repeat each failed request up to 10 times
er = EventRegistry(apiKey = YOUR_API_KEY, repeatFailedRequestCount = 10)
# an interactive application that should fail fast: don't repeat the requests
erFast = EventRegistry(apiKey = YOUR_API_KEY, repeatFailedRequestCount = 0)Sometimes the server accepts the request (status 200), but returns an error message instead of the results - for example when the query contains an invalid URI or a condition that your account is not allowed to use. The response is then a JSON object with the error property.
The iterator classes (QueryArticlesIter, QueryEventsIter, QueryMentionsIter, QueryEventArticlesIter) raise a QueryError in this case, both while iterating and in their count() method:
from eventregistry import *
er = EventRegistry(apiKey = YOUR_API_KEY)
q = QueryArticlesIter(conceptUri = er.getConceptUri("Tesla"))
try:
for art in q.execQuery(er, sortBy = "date", maxItems = 500):
print(art["title"])
except QueryError as ex:
print("The query could not be executed:", ex)EventRegistry.execQuery() by default returns the response as it is, so that you can check it yourself. Call it with raiseOnError = True to get a QueryError instead:
q = QueryArticles(keywords = "Tesla")
# default: check the response
res = er.execQuery(q)
if "error" in res:
print("error:", res["error"])
# or let the package raise the exception
res = er.execQuery(q, raiseOnError = True)Your requests use tokens (see usage tracking and the plans page). When the tokens are used up, the requests are rejected with status 401 or 403 (UnauthorizedError / ForbiddenError, both AccessError).
How to check the usage:
-
er.getUsageInfo()makes a request and returns a dict with the number of used tokens (usedTokens) and the number of tokens available in your plan (availableTokens). It can be called at any time. -
er.getRemainingAvailableRequests()ander.getDailyAvailableRequests()return the remaining and the total number of requests, as reported by the server in the headers of the last response. They don't make a request and return -1 until at least one request has been made (or if the server did not send the information). -
er.printLastReqStats()prints the number of tokens used by the last request, the performed action and whether the archive was used.er.getLastReqArchiveUse()returns True if the last request used the archive.
from eventregistry import *
er = EventRegistry(apiKey = YOUR_API_KEY)
before = er.getUsageInfo()
q = QueryArticlesIter(keywords = "Tesla", dateStart = "2026-10-01")
articles = list(q.execQuery(er, maxItems = 300))
er.printLastReqStats()
after = er.getUsageInfo()
print("tokens used by the job: %s" % (after["usedTokens"] - before["usedTokens"]))
print("tokens left: %s" % (after["availableTokens"] - after["usedTokens"]))Archive queries cost more. A query that searches the archive (content older than 31 days) uses more tokens than a query on the recent content, and the cost grows with the number of searched years. A query whose date conditions don't limit it to the recent content can search the archive too. If you are only interested in the recent content, create the instance with allowUseOfArchive = False - all the queries are then executed on the content of the last 31 days, regardless of the date conditions in the queries:
er = EventRegistry(apiKey = YOUR_API_KEY, allowUseOfArchive = False)The setting can also be overridden for a single request with er.execQuery(q, allowUseOfArchive = False).
Things that use more tokens than expected: iterating over many results (each downloaded page of results is one search), calling count() (it is a search too), and running the same query repeatedly in a loop. See the Quickstart recipes for how to estimate the number of requests.
The package writes its messages to the eventregistry logger of the Python logging module. It does not configure the logging itself (since version 11.0 it does not add a handler and ignores the LOGLEVEL environment variable), so the application decides what is shown. Without any configuration, Python prints the warnings and errors of the package to the standard error output.
To see more, configure the logging in your script:
import logging
from eventregistry import *
# show the messages of all modules from the INFO level (e.g. when a failed request will be repeated)
logging.basicConfig(level = logging.INFO)
# show also the debug messages of this package (the hosts used, the downloaded pages of the iterators, ...)
logging.getLogger("eventregistry").setLevel(logging.DEBUG)
er = EventRegistry(apiKey = YOUR_API_KEY)To hide the warnings of the package, raise the level of its logger: logging.getLogger("eventregistry").setLevel(logging.ERROR).
verboseOutput. With EventRegistry(verboseOutput = True) the package logs (at the error level) the endpoint, the parameters and the error of every failed request, including the ones that are then repeated. The API key (and the credentials in a proxyUrl parameter) are masked in the logged parameters, so the output can be shared.
Request log file. er.setLogging(True) writes the endpoint and the parameters of every request (without the API key) to the file requests_log.txt in the .eventregistry folder of your home directory (~/.eventregistry/requests_log.txt).
Other helpers:
-
print(q)orrepr(q)shows the parameters of a query and the requested result type, e.g.QueryArticles(action='getArticles', keyword='Tesla', resultType=['articles']). -
er.getUrl(q)returns the URL with the parameters of the query (without the API key). -
er.getLastException()returns the exception of the last failed request (orNone), ander.getLastHeaders()the headers of the last response.
"No API key was provided. You will be allowed to perform only a very limited number of requests per day."
The EventRegistry instance was created without a key: no apiKey argument, no EVENTREGISTRY_API_KEY environment variable and no settings.json file with the key. The requests still work, but only a very limited number per day. See Authorization for the ways to provide the key.
The query returns no results (or fewer than expected). The usual reasons are:
- The dates. Free accounts can access only the content of the last 30 days, and an instance created with
allowUseOfArchive = Falseonly the last 31 days, regardless ofdateStartanddateEnd. Check also thatdateStartis not afterdateEndand that the dates are in the YYYY-MM-DD format. - A
get*Uri()method returnedNone.er.getConceptUri("some label")returnsNonewhen nothing matches, and a condition with the valueNoneis ignored. Print the URIs before using them in a query. - The conditions are combined with AND. Each added condition (keywords, concepts, categories, sources, language, ...) reduces the results. A list of values given without
QueryItems.OR()orQueryItems.AND()uses a default operator and logs a warning - state the operator explicitly. - Keywords are by default searched as a phrase in the article body.
keywords = "Tesla Model Y"finds only the articles with these three words together. See keyword searches and concepts vs. keywords. - The sentiment filter. Setting
minSentimentormaxSentimentlimits the article results to English, since sentiment is computed only for English articles. - The data type. Articles are by default searched only among news (
dataType = "news"), not among blogs and press releases. - Since version 11.0 the iterators no longer hide server errors - if the iteration used to end silently, you now get a
QueryErrorthat explains why.
TypeError: ... got an unexpected keyword argument. A misspelled or unknown argument. Since version 11.0 the execQuery() methods of the iterator classes and RequestEventArticles accept only their documented arguments (before, unknown arguments were silently ignored, so sortby = "date" had no effect). The *InfoFlags classes raise a TypeError for an argument that differs from a known one only in the letter case (e.g. bodylen) and log a warning for other unknown arguments - see ReturnInfo.
NameError: name 'datetime' is not defined (or json, time, os, ...). Since version 11.0 from eventregistry import * imports only the public names of the package. Add import datetime (or the module that is missing) to your script.
Times are off by a few hours. Event Registry works with UTC. A datetime without a timezone is considered to be in UTC - it is not converted from your local time. A datetime with a timezone is converted to UTC. To be explicit, use timezone-aware values:
import datetime
from eventregistry import *
# the last 6 hours, regardless of the timezone of the computer
tm = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(hours = 6)
q = QueryArticles(keywords = "Tesla")
q.setRequestedResult(RequestArticlesRecentActivity(updatesAfterTm = tm))Date conditions (dateStart, dateEnd) use the UTC date of the given datetime. Strings with a timezone are accepted where a time is expected, e.g. "2026-10-02T06:30:00+02:00" or "2026-10-02T04:30:00Z".
The number of results differs between two calls of the same query. The data is live: new articles are added all the time, events are updated and merged, and duplicates are detected later. The total number of results (and count()) of the same query can therefore differ slightly from one call to the next, and also between count() and the number of items that the iterator then returns. Don't rely on the two numbers being exactly the same.
RuntimeError: Iterating is only possible after calling the execQuery() method first. You iterated over an iterator query object (for art in q:) before calling q.execQuery(er). Iterate over the value returned by execQuery(): for art in q.execQuery(er):.
ValueError about count. A single request can return at most 100 articles or mentions and 50 events. To get more, use the iterator classes or request the next pages.
The script hangs when the network is down. With repeatFailedRequestCount = -1 failed requests are repeated forever. Use the default (3) or another non-negative number, and handle the NetworkError.
Warnings about too many simultaneous requests although the script makes one request at a time. The limit is per user, not per script. Something else is using the same API key at the same time - see the limit of 5 simultaneous requests.
If you can't find the reason for a problem, open an issue on GitHub with the code that reproduces it (without your API key), the version of the package (eventregistry.__version__) and the full error message.
Core Information
Usage tracking
Terminology
EventRegistry class
ReturnInfo class
Data models for returned information
Finding concepts for keywords
Filtering content by news sources
Errors and troubleshooting
Migration guide (v10, v11)
Text analytics
Semantic annotation, categorization, sentiment
Searching
Searching for events
Searching for articles
Searching for mentions
Article/event info
Get event information
Get article information
Other
Supported languages
Different ways to search using keywords
Topic pages
Feed of new articles/events
Daily trends
Number of mentions in the news
Find the event for your own text
Article URL to URI mapping
API reference