Google Reader Python API Client
This project is a Python client library for feed readers that utilize the Google Reader API. Although Google Reader was discontinued in 2013, many feed readers continue to rely on this API.
Note that this library is not fully implemented. It has been tested with Miniflux and FreshRSS.
There is no official Google Reader API documentation. This library has been developed by analyzing existing software implementations.
Installation
python3 -m pip install google-reader
Usage Example
>>> import google_reader
>>> client = google_reader.Client("https://reader.example.org/")
>>> auth_token = client.login("my_username", "my_password")
>>> client.get_user_info(auth_token)
UserInfo(user_id='1', user_name='admin', user_email='admin@example.org', user_profile_id='1')
>>> client.list_subscriptions(auth_token)
[Subscription(id='feed/6', title='Feed Title', url='https://example.org/feed', html_url='https://example.org/', icon_url='https://reader.example.org/feed/icon/846d0be14a9676baabfb7fbb69a1fbca8bb9dad3', categories=[Category(id='user/1/label/My Test', label='My Test', type='folder')])]
Documentation
- Item IDs Format
- Stream IDs
- Client Login
- Token
- User Information
- Subscriptions List
- Edit Subscription
- Quick Add Subscription
- Get Stream Items IDs
- Get Stream Items Contents
- Edit Tags
- Delete Tag
- Rename Tag
- List Tags
- Mark all as read
- Resources
Item IDs Format
Item IDs can be returned in short or long form depending on the API method.
- Long form: The prefix
tag:google.com,2005:reader/item/followed by the ID as an unsigned base 16 number that is 0-padded so that it's always 16 characters wide. - Short form: The ID as a signed base 10 number.
Examples:
tag:google.com,2005:reader/item/00000000148b9369 344691561
tag:google.com,2005:reader/item/00000000148b383e 344668222
tag:google.com,2005:reader/item/00000000148b3841 344668225
Stream IDs
Streams can be feeds, tags (labels/folders) or states.
- Feed:
feed/http://example.org/feedorfeed/123456 - Tag:
user/-/label/Some Text - Read articles:
user/-/state/com.google/read - Starred articles:
user/-/state/com.google/starred - Broadcasted articles:
user/-/state/com.google/broadcast - Annotated articles:
user/-/state/com.google/annotated - Likes articles:
user/-/state/com.google/like - Saved web pages:
user/-/state/com.google/saved-web-pages
Client Login
Request:
- URL:
/accounts/ClientLogin - Method:
GET
Querystring parameters:
Email: the user's emailPasswd: the user's account password
The plain text response contains 3 lines:
SID=...
LSID=...
Auth=<token>
The access token can be retrieved from the third line of the response and should be included in the Authorization header for each request:
Authorization: GoogleLogin auth=<token>
Some endpoints require the POST parameter T to include the access token.
Python example:
>>> import google_reader
>>> client = google_reader.Client("https://reader.example.org/")
>>> client.login("my_username", "my_password")
AuthToken(TokenType='GoogleLogin', AccessToken='test/8eec3f60a23a5b5464245054f60eb9ced8d5655c')
Token
This endpoint returns a CSRF token required for making modifications.
The token must be included as the T parameter in POST requests, along with the Authorization header.
Request:
- URL:
/reader/api/0/token - Method:
GET - Supported formats: Returns the CSRF token in plain text format
Python example:
>>> import google_reader
>>> client = google_reader.Client("https://reader.example.org/")
>>> auth_token = client.login("my_username", "my_password")
>>> client.get_token(auth_token)
'ccd127a64a7150c5fae14e5699b3df6751c083d4ZZZZZZZZZZZZZZZZZ'
User Information
Request:
- URL:
/reader/api/0/user-info - Method:
GET - Supported formats: JSON
Response:
{
"userId": "1234",
"userName": "test",
"userProfileId": "1234",
"userEmail": "test@example.org",
"isBloggerUser": true,
"signupTimeSec": 1163850013,
"isMultiLoginEnabled": false
}
Python example:
>>> import google_reader
>>> client = google_reader.Client("https://reader.example.org/")
>>> auth_token = client.login("my_username", "my_password")
>>> client.get_user_info(auth_token)
UserInfo(user_id='1234', user_name='test', user_email='test@example.org', user_profile_id='1234')
Subscriptions List
Request:
- URL:
/reader/api/0/subscription/list - Method:
GET - Supported formats: JSON
Response:
{
"subscriptions": [
"id": "feed/2",
"title": "Kanboard Release Notes",
"url": "https://kanboard.org/releases.xml"
"htmlUrl": "https://kanboard.org/releases.html",
"iconUrl": "http://localhost/feed/icon/d8276d41bd515db917b46a38d743932d0d02da51",
"categories": [
{
"id": "user/1/label/All",
"label": "All",
"type": "folder"
}
]
]
}
Python Example:
>>> import google_reader
>>> client = google_reader.Client("https://reader.example.org/")
>>> auth_token = client.login("my_username", "my_password")
>>> client.list_subscriptions(auth_token)
[Subscription(id='feed/1', title='FreshRSS releases', url='https://github.com/FreshRSS/FreshRSS/releases.atom', html_url='https://github.com/FreshRSS/FreshRSS/', icon_url='http://127.0.0.1:8082/f.php?516d4293', categories=[Tag(id='user/-/label/Uncategorized', label='Uncategorized', type=None)])]
Edit Subscription
Request:
- URL:
/reader/api/0/subscription/edit - Method:
POST - Supported formats: Returns "OK" in plain text
- CSRF token required: Yes
Creation:
ac: the stringsubscribes: the stream ID to create (feed/<feed url>)t: the name for this subscriptiona: the stream ID of a category. If the category doesn’t exist, it will be created (optional)
Edition:
ac: the stringedits: the stream ID to edit (feed/<feed url>)rora: the stream ID of a category.rmoves the feed out of the category,aadds the feed to the categoryt: a new title for the feed
Deletion:
ac: the stringunsubscribes: the stream ID to delete (feed/<feed url>)
Python Example:
>>> import google_reader
>>> client = google_reader.Client("https://reader.example.org/")
>>> auth_token = client.login("my_username", "my_password")
>>> csrf_token = client.get_token(auth_token)
>>> client.edit_subscription(auth_token, csrf_token, subscription_id="feed/1", action="edit", add_label_id=google_reader.get_label_id("Test"))
True
>>> c.edit_subscription(auth_token, csrf_token, subscription_id="feed/1", action="edit", title="New Feed Title")
True
Quick Add Subscription
Request:
- URL:
/reader/api/0/subscription/quickadd - Method:
POST - Supported formats: JSON
- CSRF token required: Yes
Response:
{
"query": "feed/http://feeds.arstechnica.com/arstechnica/science",
"numResults": 1,
"streamId": "feed/http://arstechnica.com/",
"streamName": "Ars Technica » Scientific Method"
}
Python Example:
>>> import google_reader
>>> client = google_reader.Client("https://reader.example.org/")
>>> auth_token = client.login("my_username", "my_password")
>>> csrf_token = client.get_token(auth_token)
>>> client.quick_add_subscription(auth_token, csrf_token, url="https://lemonde.fr")
QuickAddSubscription(query='https://www.lemonde.fr/rss/une.xml', num_results=1, stream_id='feed/2', stream_name='Le Monde.fr - Actualités et Infos en France et dans le monde'
Get Stream Items IDs
Request:
- URL:
/reader/api/0/stream/items/ids - Method:
GET - Supported formats: JSON
Querystring parameters:
s: Stream IDn: Number of items to returnr: Order direction. By default, it is newest first. You can pass the valueohere to get oldest first.ot: Start time (unix timestamp) from which to start to get items.xt: Exclude Target. For example, set touser/-/state/com.google/readto exclude all read items.it: Include Target. For example, set touser/-/state/com.google/starredto include starred items.c: Continuation (pagination cursor).
Response:
{
"itemRefs": [
{"id": "123"},
{"id": "456"}
],
"continuation": "abcde"
}
Python example:
>>> import google_reader
>>> client = google_reader.Client("https://reader.example.org/")
>>> auth_token = client.login("my_username", "my_password")
>>> client.get_stream_items_ids(auth_token, stream_id="feed/1", limit=2)
StreamIDs(item_refs=[ItemRef(id='1746307865812560'), ItemRef(id='1746307865812559')], continuation='1746307865812559')
Get Stream Items Contents
Request:
- URL:
/reader/api/0/stream/items/contents - Methods:
GET - Supported formats: JSON
Parameter:
i: Item ID (can be repeated)
Response:
{
"alternate": [
{
"href": "https://miniflux.app/",
"type": "text/html"
}
],
"author": "admin",
"direction": "ltr",
"id": "feed/22",
"items": [
{
"alternate": [
{
"href": "https://miniflux.app/releases/2.2.8.html",
"type": "text/html"
}
],
"author": "Myself",
"canonical": [
{
"href": "https://miniflux.app/releases/2.2.8.html"
}
],
"categories": [
"user/1/state/com.google/reading-list",
"user/1/label/All"
],
"content": {
"content": "long text",
"direction": "ltr"
},
"crawlTimeMsec": "1746310821141",
"enclosure": [
{
"type": "application/octet-stream",
"url": "https://github.com/miniflux/v2/releases/download/2.2.8/miniflux-linux-amd64"
},
{
"type": "application/octet-stream",
"url": "https://github.com/miniflux/v2/releases/download/2.2.8/miniflux-linux-amd64.sha256"
}
],
"id": "tag:google.com,2005:reader/item/0000000000000468",
"origin": {
"htmlUrl": "https://miniflux.app/",
"streamId": "feed/22",
"title": "Miniflux"
},
"published": 1745280000,
"summary": {
"content": "long text",
"direction": "ltr"
},
"timestampUsec": "1745280000000000",
"title": "Miniflux 2.2.8",
"updated": 1746310821
}
],
"self": [
{
"href": "http://localhost/reader/api/0/stream/items/contents"
}
],
"title": "Miniflux",
"updated": 1746311274
}
Python example:
>>> import google_reader
>>> client = google_reader.Client("https://reader.example.org/")
>>> auth_token = client.login("my_username", "my_password")
>>> c.get_stream_items_contents(auth_token, item_ids=[google_reader.get_long_item_id(1128)])
StreamContentItems(...)
Edit Tags
Request:
- URL:
/reader/api/0/edit-tag - Method:
POST - Supported formats: Returns "OK" in plain text
- CSRF token required: Yes
Parameters:
i: Item ID (can be repeated)a: tag to add to the items (can be repeated)r: tag to remove from the items (can be repeated)
Possible tags are:
user/-/state/com.google/kept-unreaduser/-/state/com.google/starreduser/-/state/com.google/broadcastuser/-/state/com.google/readuser/-/state/com.google/likeuser/-/label/tag_name
Python example:
>>> import google_reader
>>> client = google_reader.Client("https://reader.example.org/")
>>> auth_token = client.login("my_username", "my_password")
>>> csrf_token = client.get_token(auth_token)
>>> client.edit_tags(auth_token, csrf_token, item_ids=[google_reader.get_long_item_id(1128)], add_tags=[google_reader.STREAM_STARRED])
True
>>> client.edit_tags(auth_token, csrf_token, item_ids=['1746307865812560'], add_tags=[google_reader.STREAM_STARRED])
True
Delete Tag
Request:
- URL:
/reader/api/0/disable-tag - Method:
POST - Supported formats: Returns "OK" in plain text
- CSRF token required: Yes
Parameters:
s: Stream ID
Python example:
>>> import google_reader
>>> client = google_reader.Client("https://reader.example.org/")
>>> auth_token = client.login("my_username", "my_password")
>>> csrf_token = client.get_token(auth_token)
>>> client.disable_tag(auth_token, csrf_token, tag_id='user/-/label/Test')
True
>>> client.delete_tag(auth_token, csrf_token, tag_id='user/-/label/Test')
True
Rename Tag
Request:
- URL:
/reader/api/0/rename-tag - Method:
POST - Supported formats: Returns "OK" in plain text
- CSRF token required: Yes
Parameters:
s: Tag stream IDdest: New label name, for example:user/-/label/<new label>
Python example:
>>> import google_reader
>>> client = google_reader.Client("https://reader.example.org/")
>>> auth_token = client.login("my_username", "my_password")
>>> csrf_token = client.get_token(auth_token)
>>> client.rename_tag(auth_token, csrf_token, tag_id='user/-/label/All', new_label_name="Something Else")
True
List Tags
Request:
- URL:
/reader/api/0/disable-tag - Method:
GET - Supported formats: JSON
Response:
{
"tags": [
{"id": "user/1005921515/state/com.google/starred"},
{"id": "user/1/label/My Test", "label": "My Test", "type": "folder"}
]
}
Python example:
>>> import google_reader
>>> client = google_reader.Client("https://reader.example.org/")
>>> auth_token = client.login("my_username", "my_password")
>>> client.list_tags(auth_token)
[Tag(id='user/-/state/com.google/starred', label=None, type=None), Tag(id='user/-/label/Test', label=None, type='folder'), Tag(id='user/-/label/Uncategorized', label=None, type='folder')]
>>> client.list_tags(auth_token)
[Tag(id='user/1/state/com.google/starred', label=None, type=None), Tag(id='user/1/label/All', label='All', type='folder'), Tag(id='user/1/label/My Test', label='My Test', type='folder')]
Mark all as read
Request:
- URL:
/reader/api/0/mark-all-as-read - Method:
POST - Supported formats: Returns "OK" in plain text
- CSRF token required: Yes
Parameters:
s: Stream IDts(optional): Unix Timestamp in seconds or microseconds. When provided, only items older than this timestamp are marked as read.
Python example:
>>> import google_reader
>>> client = google_reader.Client("https://reader.example.org/")
>>> auth_token = client.login("my_username", "my_password")
>>> csrf_token = client.get_token(auth_token)
>>> client.mark_all_as_read(auth_token, csrf_token, 'feed/1')
True
Resources
Metadata
Release files for google-reader 0.0.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| google_reader-0.0.3.tar.gz | 10.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| google_reader-0.0.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 20.2 kB
Release files / google_reader-0.0.3.tar.gz
| Download URL | google_reader-0.0.3.tar.gz |
|---|---|
| Size | 10.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
397d65d772d353f0be3137b05f4a4d0ca5a4d5c0545456769f495e37c0178629
|
|
BLAKE2b-256 checksum How to use checksums |
370e72617daa38fa0eaa5ad6e9e6ba25a6dd1186595fbd19e65b70104c799811
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.12.9
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 5, 2025.
Transparency logRelease files / google_reader-0.0.3-py3-none-any.whl
| Download URL | google_reader-0.0.3-py3-none-any.whl |
|---|---|
| Size | 10.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7747c1b48e72a3b988364211c13e7f5d30655023102ef64e5177f532851a1436
|
|
BLAKE2b-256 checksum How to use checksums |
32ae2750424c1ca9d3be30036ed7091e8288bc9754450719f1000067f7c84117
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.12.9
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 5, 2025.
Transparency log