Skip to content

Instantly share code, notes, and snippets.

Last active September 11, 2023 16:47
Show Gist options
  • Save hogseedy/b149c5f1ad1b628ba00556c7d4a898f8 to your computer and use it in GitHub Desktop.
Save hogseedy/b149c5f1ad1b628ba00556c7d4a898f8 to your computer and use it in GitHub Desktop.
Reddit native auth / GQL example
import requests
import hashlib
import base64
import uuid
import time
import json
import hmac
# Android app api keys, posted here:
REDDIT_ANDROID_HMAC_SECRET = '8c7abaa5f905f70400c81bf3a1a101e75f7210104b1991f0cd5240aa80c4d99d'
class Reddit:
USER_AGENT = 'Reddit/Version 2023.21.0/Build 956283/Android 13'
def __init__(self, client_id, hmac_secret):
self.client_id = client_id
self.hmac_secret = hmac_secret
self.token = None
self.s = requests.session()
self.uuid = str(uuid.uuid4())
'Client-Vendor-Id': self.uuid,
'X-Reddit-Device-Id': self.uuid,
'User-Agent': self.USER_AGENT
def login_anonymous(self):
return self._get_token()
def login_user(self, username, password):
epoch = str(int(time.time()))
body = json.dumps({'username': username, 'password': password}, separators = (',', ':'))
body_sig = f'1:android:2:{epoch}:{self._generate_body_sig(epoch, body)}'
result_sig = f'1:android:2:{epoch}:{self._generate_result_sig(epoch)}'
r =
data = body,
headers = {
'X-Hmac-Signed-Body': body_sig,
'X-Hmac-Signed-Result': result_sig
if r.json()['success']:
print('reddit_session =', r.cookies['reddit_session'])
print('modhash =', r.json()['modhash'])
return self._get_token()
raise Exception('Login error')
def gql_get_posts(self, subreddit, sort='HOT', after=None):
if not self.token:
raise Exception('Not authorized')
json_body = {
'id': '3496a5858eb9', # SubredditFeedElements
'variables': {
'subredditName': subreddit,
'sort': sort,
'adContext': {
'distance': None,
'layout': 'CARD',
'deviceAdId': '',
'clientSignalSessionData': {
'adsSeenCount': 123,
'totalPostsSeenCount': 456,
'sessionStartTime': '2023-06-05T12:34:56.000000+0000'
'forceAds': {},
'feedFilters': {},
'optedIn': True,
'includeSubredditInPosts': False,
'includeAwards': False,
'feedContext': {
'experimentOverrides': []
'includePostStats': False
if after:
json_body['variables']['after'] = base64.b64encode(after.encode()).decode()
r =
json = json_body
return r.json()
def _get_token(self):
r =
json = {
'scopes': ['*','email','pii']
auth = requests.auth.HTTPBasicAuth(self.client_id, '')
if 'access_token' in r.json():
self.token = r.json()['access_token']
self.s.headers.update({'Authorization': f'Bearer {self.token}'})
return True
raise Exception('Login error')
def _generate_body_sig(self, epoch, body):
return self._hmac_sign_data(f'Epoch:{epoch}|Body:{body}')
def _generate_result_sig(self, epoch):
return self._hmac_sign_data(f'Epoch:{epoch}|User-Agent:{self.USER_AGENT}|Client-Vendor-ID:{self.uuid}')
def _hmac_sign_data(self, data):
return, msg = data.encode(), digestmod = hashlib.sha256).hexdigest()
if __name__ == '__main__':
r.login_anonymous() # r.login_user('user', 'pass')
print(r.gql_get_posts('technology', after='t3_13zvyn5'))
Copy link

Thanks for this!

Copy link

juanpnvd commented Jul 5, 2023

This is awesome, thank you. Is there a way to save the session cookies and load it to use it after?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment