Skip to content
Gregor Leban edited this page Oct 14, 2026 · 1 revision

This page describes what you have to change in your code when you update the eventregistry package to a new major version. It covers the two latest major versions:

If you are updating from version 9 directly to version 11, go through both sections. The complete list of changes of each version is in the change log.

To see which version you have installed:

import eventregistry
print(eventregistry.__version__)

Quick checklist

v10 to v11

  • You use Python 3.9 or newer.
  • Scripts that use from eventregistry import * import datetime, json, time, os, ... themselves.
  • Loops over QueryArticlesIter, QueryEventsIter, QueryMentionsIter and QueryEventArticlesIter (and calls of their count()) handle QueryError.
  • Calls of execQuery() on the iterator classes and of RequestEventArticles() use only existing argument names (a misspelled one now raises a TypeError).
  • The arguments of the *InfoFlags classes are spelled with the right letter case (bodyLen, not bodylen).
  • If you relied on the package to set up logging or on the LOGLEVEL environment variable, you configure the logging module yourself.
  • If you relied on failed requests being repeated forever, you create EventRegistry(repeatFailedRequestCount = -1) - or, better, handle the exceptions.
  • Code that inspected the text of the raised exception uses the typed exceptions and their statusCode instead.
  • Code that compared the sent time values with strings that contain a timezone offset expects UTC without the offset.
  • GetTopSharedArticles, GetTopSharedEvents and RequestMentionsConceptGraph are not used.

v9 to v10

  • You use Python 3 (Python 2 is not supported anymore).
  • except AssertionError around calls of the package is replaced with except (TypeError, ValueError).
  • RequestEventsEventClusters and the Analytics.trainTopic*() methods are not used.
  • EventRegistry.getArticleUris() is called with a single URL.
  • RequestArticlesInfo and RequestMentionsInfo are created with count of at most 100.
  • The iterator classes are iterated only after calling execQuery().
  • If you depended on the old default hosts, or on settings.json / the request log being in the package folder, you checked the new defaults.

Migrating from v10 to v11

Python 3.9 or newer is required

Version 10 supported Python 3.8. Update Python, or stay with pip install "eventregistry<11" on Python 3.8.

from eventregistry import * imports only the public names

Before, the star import also brought along the modules that the package uses itself (datetime, json, os, time, re, requests, ...). This could overwrite names in your code, for example a datetime imported with from datetime import datetime. Now only the public classes, functions and constants of the package are imported.

What to do: import the standard modules that your script uses.

# before: datetime came from the star import
from eventregistry import *
q = QueryArticles(keywords = "Tesla", dateStart = datetime.date(2026, 9, 1))
# after
import datetime
from eventregistry import *
q = QueryArticles(keywords = "Tesla", dateStart = datetime.date(2026, 9, 1))

A missing import shows up as NameError: name 'datetime' is not defined.

The iterators raise QueryError instead of silently ending

When the server returned an error (for example because of an invalid URI in the query), the iterator classes only logged it and the iteration ended as if there were no (more) results; count() returned 0. Now a QueryError is raised, both while iterating and in count().

What to do: decide what should happen when the query fails, and catch the exception where needed.

# before: an error looked the same as "no results"
from eventregistry import *
er = EventRegistry(apiKey = YOUR_API_KEY)
q = QueryArticlesIter(conceptUri = er.getConceptUri("Tesla"))
for art in q.execQuery(er, sortBy = "date", maxItems = 500):
    print(art["title"])
# after
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 failed:", ex)

execQuery() of the iterators and RequestEventArticles reject unknown arguments

The execQuery() methods of the iterator classes accepted arbitrary keyword arguments and silently ignored them, so a misspelled argument had no effect. The same was true for RequestEventArticles. Now an unknown argument raises a TypeError.

What to do: fix the name of the argument. The accepted arguments of execQuery() are sortBy, sortByAsc, returnInfo and maxItems.

# before: "sortby" was ignored and the articles were sorted by relevance
for art in q.execQuery(er, sortby = "date", maxItems = 100):
    print(art["title"])
# after
for art in q.execQuery(er, sortBy = "date", maxItems = 100):
    print(art["title"])

Conditions that limit the articles of an event (keywords, sources, language, ...) are given to the constructor of QueryEventArticlesIter, not to execQuery():

# after
iter = QueryEventArticlesIter("eng-2940883", lang = "eng", keywords = "Obama")
for art in iter.execQuery(er, sortBy = "date"):
    print(art["title"])

The *InfoFlags classes check the names of the arguments

The *InfoFlags classes (ArticleInfoFlags, ConceptInfoFlags, ...) send unknown keyword arguments to the server as they are, and the server ignores the flags that it does not know. Now an argument that differs from a known one only in the letter case raises a TypeError, and for other unknown arguments a warning is logged (they are still sent to the server). The full names of the API parameters (starting with include) are accepted without a warning.

# before: no effect - the full body was returned
info = ReturnInfo(articleInfo = ArticleInfoFlags(bodylen = 500))
# after ("bodylen" raises a TypeError)
info = ReturnInfo(articleInfo = ArticleInfoFlags(bodyLen = 500))

The package no longer configures logging

Before, the package added its own handler to the eventregistry logger and read the logging level from the LOGLEVEL environment variable. As a library it now leaves the configuration to the application. Warnings and errors are still printed by default (by Python itself), but info and debug messages are shown only if you configure the logging.

# before: the level was set with the LOGLEVEL environment variable, e.g. LOGLEVEL=DEBUG
from eventregistry import *
er = EventRegistry(apiKey = YOUR_API_KEY)
# after
import logging
from eventregistry import *
logging.basicConfig(level = logging.INFO)
logging.getLogger("eventregistry").setLevel(logging.DEBUG)
er = EventRegistry(apiKey = YOUR_API_KEY)

Times with a timezone are sent as UTC without the offset

A datetime with a timezone is converted to UTC and sent without the offset (2026-10-02T04:30:00 instead of 2026-10-02T04:30:00+00:00). A datetime without a timezone is considered to be in UTC, as before. For date conditions (dateStart, dateEnd, ...) the UTC date of a timezone-aware datetime is used (before, its local date was used). Strings with Z or an offset ("2026-10-02T06:30:00+02:00") are now accepted where a time is expected.

What to do: usually nothing. If you passed timezone-aware datetimes, check that the conversion to UTC is what you want; if your code inspects the query parameters (for example in tests), expect the new format.

# before: sent as 2026-10-02T06:30:00+02:00
# after:  sent as 2026-10-02T04:30:00
import datetime
from eventregistry import *
tz = datetime.timezone(datetime.timedelta(hours = 2))
req = RequestArticlesRecentActivity(updatesAfterTm = datetime.datetime(2026, 10, 2, 6, 30, tzinfo = tz))

Failed requests are repeated 3 times, not forever

The default value of the repeatFailedRequestCount parameter of EventRegistry changed from -1 (repeat indefinitely) to 3. In addition:

  • only temporary errors are repeated: status 5xx, a response that is not valid json, connection problems and timeouts. Before, every status code except 204, 400, 401 and 403 was repeated (for example also 404 and 422);
  • status 429 (too many simultaneous requests) is still repeated until the request succeeds, regardless of repeatFailedRequestCount;
  • the delay before a repeat grows exponentially (2, 4, 8, ... seconds, at most 60) instead of a fixed 5 seconds, and follows the Retry-After header of the server when present.

What to do: a script that ran unattended and relied on the package to wait until the service is reachable again will now get an exception after about 14 seconds of waiting (2 + 4 + 8). Handle the exception, increase the number of repeats, or restore the old behavior:

# before: repeated forever by default
from eventregistry import *
er = EventRegistry(apiKey = YOUR_API_KEY)
# after: the old behavior has to be requested
from eventregistry import *
er = EventRegistry(apiKey = YOUR_API_KEY, repeatFailedRequestCount = -1)

Failed requests raise typed exceptions

A failed request used to raise a plain Exception with the status code in its text. Now the exceptions are subclasses of EventRegistryError: NoDataError (204), InvalidRequestError (400 and other 4xx), UnauthorizedError (401) and ForbiddenError (403) (both are AccessError), RateLimitError (429), ServerError (5xx), InvalidResponseError (invalid json) and NetworkError (connection problems and timeouts). They have the statusCode, responseText, url and retryAfter attributes. The requests of the Analytics class raise the same exceptions.

What to do: nothing, if you catch Exception - the new exceptions derive from it. Code that looked for the status code in the text of the exception should use the exception classes or the statusCode attribute. Code that caught the exceptions of the requests library (e.g. requests.exceptions.ConnectionError) around calls of the package should catch NetworkError.

# before
try:
    res = er.execQuery(q)
except Exception as ex:
    if "401" in str(ex) or "403" in str(ex):
        print("no more tokens")
    else:
        raise
# after
try:
    res = er.execQuery(q)
except AccessError as ex:
    print("no more tokens:", ex.responseText)

See Errors and troubleshooting for the details.

execQuery() of the iterators returns a separate iterator object

execQuery() of the iterator classes returned the query object itself; now it returns a separate iterator (QueryResultsIter) and the query is not changed by iterating. The usual code (for art in q.execQuery(er):) works as before, and iterating over the query object after calling execQuery() still works too. The new behavior makes it possible to execute the same query several times, also at the same time:

# after: two independent iterations over the same query
newest = q.execQuery(er, sortBy = "date", maxItems = 10)
mostRelevant = q.execQuery(er, sortBy = "rel", maxItems = 10)
for artNew, artRel in zip(newest, mostRelevant):
    print(artNew["title"], "|", artRel["title"])

What to do: only code that called methods of the query on the value returned by execQuery() has to be changed - call them on the query object.

GetTopSharedArticles, GetTopSharedEvents and RequestMentionsConceptGraph were removed

The data about the shares on social media is no longer collected and the server ignores sorting by the social score, so GetTopSharedArticles and GetTopSharedEvents did not return the most shared content anymore - only the articles or events of the given day in no particular order. RequestMentionsConceptGraph was never supported by the server and only returned an error.

What to do: if you need the articles or the events of a day, search for them with QueryArticles / QueryEvents and the dateStart and dateEnd conditions.

# before
q = GetTopSharedArticles(date = "2026-10-01", count = 30)

# after
q = QueryArticles(dateStart = "2026-10-01", dateEnd = "2026-10-01",
    requestedResult = RequestArticlesInfo(count = 30, sortBy = "sourceImportance"))

Smaller changes that can affect the results

  • QueryEvents sent the ignoreKeywords condition with a wrong parameter name. It is fixed, so queries for events that use ignoreKeywords can now return fewer events.
  • QueryEventArticlesIter replaced the QueryItems.OR() / QueryItems.AND() operators of the conditions with the default ones. The operators that you specify are now used.
  • An empty list (or an empty QueryItems) given as a condition is ignored instead of being sent to the server.
  • The example file examples/QueryMentions.py was renamed to QueryMentionsExamples.py. The example files now contain functions and make no requests when they are imported or run without arguments.

New features that you may want to use

None of these require a change, but they can simplify your code:

  • The API key can be given with the EVENTREGISTRY_API_KEY environment variable - see Authorization.
  • er.execQuery(q, raiseOnError = True) raises a QueryError when the server returns an error instead of the results.
  • GetRecentArticles and GetRecentEvents accept the conditions as named parameters - see Feed of new articles/events.
# before: raw API parameters
recentQ = GetRecentArticles(er, recentActivityArticlesMaxArticleCount = 200, conceptUri = er.getConceptUri("Tesla"))
# after: named parameters, validated in the same way as in QueryArticles
recentQ = GetRecentArticles(er, maxArticleCount = 200, conceptUri = er.getConceptUri("Tesla"))
  • TopicPage.getArticles() and getEvents() have the uri, onlyAfterTm and maxDaysBack parameters - see Topic pages.
  • New result types: RequestArticlesLangAggr, RequestArticlesSentimentAggr, RequestArticlesAuthorAggr, RequestArticlesLocAggr, RequestArticlesSourceExAggr, RequestEventsSentimentAggr and RequestEventsAuthorAggr.
  • BaseQuery has the keywordSearchMode parameter.
  • The package contains type information (py.typed), so type checkers such as mypy use its type hints.

Migrating from v9 to v10

Python 2 is no longer supported

Version 10.0 requires Python 3.8 or newer (version 11.0 requires Python 3.9). The six and pytz dependencies were removed - if your own code imports them, add them to your own requirements. If you have to stay on Python 2, use pip install "eventregistry<10".

Invalid arguments raise TypeError or ValueError instead of AssertionError

The arguments were checked with assert statements. Now a wrong type raises a TypeError and an invalid value raises a ValueError (also when Python runs with the -O option, which removes the asserts).

# before
try:
    q = QueryArticles(keywords = "Tesla", minSentiment = -2)
except AssertionError:
    print("invalid query")
# after
try:
    q = QueryArticles(keywords = "Tesla", minSentiment = -2)
except (TypeError, ValueError) as ex:
    print("invalid query:", ex)

RequestEventsEventClusters was removed

The result type is not available anymore.

# before
q = QueryEvents(conceptUri = er.getConceptUri("Obama"))
q.setRequestedResult(RequestEventsEventClusters())

What to do: remove its use. To get an overview of a larger set of events, use one of the aggregates instead, for example the top concepts or categories of the events:

# after
q = QueryEvents(conceptUri = er.getConceptUri("Obama"))
q.setRequestedResult(RequestEventsConceptAggr(conceptCount = 50))
res = er.execQuery(q)

The topic training methods of Analytics were removed

Analytics.trainTopicOnTweets(), trainTopicCreateTopic(), trainTopicClearTopic(), trainTopicAddDocument() and trainTopicGetTrainedTopic() were removed. There is no replacement in the Analytics class.

# before
analytics = Analytics(er)
ret = analytics.trainTopicCreateTopic("my topic")

What to do: remove the calls. To follow the content about a topic described with weighted concepts and keywords, use a topic page:

# after
topic = TopicPage(er)
topic.addConcept(er.getConceptUri("renewable energy"), 50)
topic.addKeyword("photovoltaic", 20)
res = topic.getArticles(page = 1)

EventRegistry.getArticleUris() accepts a single URL

The method accepted a list of URLs, but the endpoint maps one URL per call. It now accepts only a string.

# before
res = er.getArticleUris(["https://www.example.com/article1", "https://www.example.com/article2"])
# after
uris = {}
for url in ["https://www.example.com/article1", "https://www.example.com/article2"]:
    uris.update(er.getArticleUris(url))

You can also use the ArticleMapper class, which remembers the mappings.

RequestArticlesInfo and RequestMentionsInfo reject count above 100

The API never returned more than 100 articles or mentions per call, so a higher count was misleading. It now raises a ValueError.

# before: only 100 articles were returned anyway
q.setRequestedResult(RequestArticlesInfo(count = 200))
# after: ask for the pages, or use the iterator that does it for you
q.setRequestedResult(RequestArticlesInfo(page = 1, count = 100))
res1 = er.execQuery(q)
q.setRequestedResult(RequestArticlesInfo(page = 2, count = 100))
res2 = er.execQuery(q)

q = QueryArticlesIter(keywords = "Tesla")
for art in q.execQuery(er, maxItems = 200):
    print(art["title"])

Iterating before calling execQuery() raises a RuntimeError

Iterating over an instance of QueryArticlesIter, QueryEventsIter, QueryMentionsIter or QueryEventArticlesIter without calling execQuery() first now raises a RuntimeError.

# before: wrong use that did not report an error
q = QueryArticlesIter(keywords = "Tesla")
for art in q:
    print(art["title"])
# after
q = QueryArticlesIter(keywords = "Tesla")
for art in q.execQuery(er):
    print(art["title"])

The default hosts changed

The default hosts are now https://eventregistry.org and https://analytics.eventregistry.org. What to do: nothing in most cases. If your network setup (firewall or proxy rules) allowed only the previously used addresses, allow these two. If you need a different host, pass it with the host and hostAnalytics arguments or set it in settings.json:

# after: only if you need a host different from the default one
er = EventRegistry(apiKey = YOUR_API_KEY, host = "https://eventregistry.org", hostAnalytics = "https://analytics.eventregistry.org")

settings.json and the request log moved to ~/.eventregistry/

The settings.json file with the API key is now first looked up in the .eventregistry folder of your home directory and only then in the folder of the package. The request log (turned on with EventRegistry.setLogging(True)) is written to ~/.eventregistry/requests_log.txt instead of the package folder.

What to do: a settings.json in the package folder still works, unless there is also one in ~/.eventregistry/ - that one is now used. Since the package folder is replaced when the package is updated, move the file to ~/.eventregistry/settings.json. If you read the request log, look for it in the new place.

# before and after: the code is the same, only the location of the file changed
from eventregistry import *
er = EventRegistry()        # reads the key from ~/.eventregistry/settings.json
er.setLogging(True)         # writes to ~/.eventregistry/requests_log.txt

Fixes that can change the behavior of existing code

  • TopicPage.setArticleIsDuplicateFilter() wrote the value under a wrong key and the filter was ignored by the server. It now works, so topic pages that set it can return different articles.
  • ReturnInfo.getConf() exported the article flags under a wrong name.
  • count() on the iterator classes no longer changes the requested result type of the query.
  • Iterating over an iterator class a second time starts again from the first result.
  • The first request of an EventRegistry instance is no longer delayed by minDelayBetweenRequests.
  • KeyboardInterrupt (Ctrl+C) is no longer swallowed by the retry loop, so a script that repeats a failed request can be stopped.
  • The API key is no longer written into the parameter dict that you pass to jsonRequest().

Clone this wiki locally