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.
python3 -m pip install google-reader
>>> 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')])]
- 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 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
Streams can be feeds, tags (labels/folders) or states.
- Feed:
feed/http://example.org/feed
orfeed/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
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')
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'
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')
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)])]
Request:
- URL:
/reader/api/0/subscription/edit
- Method:
POST
- Supported formats: Returns "OK" in plain text
- CSRF token required: Yes
Creation:
ac
: the stringsubscribe
s
: 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 stringedit
s
: the stream ID to edit (feed/<feed url>
)r
ora
: the stream ID of a category.r
moves the feed out of the category,a
adds the feed to the categoryt
: a new title for the feed
Deletion:
ac
: the stringunsubscribe
s
: 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
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'
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 valueo
here 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/read
to exclude all read items.it
: Include Target. For example, set touser/-/state/com.google/starred
to 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')
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(...)
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-unread
user/-/state/com.google/starred
user/-/state/com.google/broadcast
user/-/state/com.google/read
user/-/state/com.google/like
user/-/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
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
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
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')]
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