Redditapis Answers

How do you use the Reddit API in Python?

To use the Reddit API in Python, send an HTTP GET to api.redditapis.com with your bearer token in the Authorization header using the requests library. There is no PRAW and no OAuth flow: you sign up, copy your token, and call the REST endpoint. The response is clean JSON you parse with response.json(). A read costs $0.002, and you start with $0.50 in free credits. The examples below call our platform's 52 REST endpoints, per our published API reference.

The full Python example

One import, one GET, and you have parsed JSON. No PRAW, no OAuth client, no refresh loop.

import requests

res = requests.get(
    "https://api.redditapis.com/api/reddit/posts",
    params={"subreddit": "programming", "sort": "new", "limit": 25},
    headers={"Authorization": "Bearer YOUR_TOKEN"},
)
res.raise_for_status()
data = res.json()

posts = data["posts"]
print(f"Got {len(posts)} posts")
print(posts[0]["title"], posts[0]["upvotes"], posts[0]["url"])

Two habits in that snippet are worth keeping. Hand the query string to params as a dict rather than building the URL yourself, and call raise_for_status() before you touch the body. The section on wasted credits below is mostly about what happens when you skip the first one.

Reddit API in Python: one import (requests), zero OAuth steps, $0.002 per read
One import, zero OAuth steps, $0.002 a read. The requests library is all you need.

What one post actually contains

Every row in data["posts"] is a flat dict. Nothing is nested behind a data wrapper and nothing needs a second call to become readable, which is the part of Reddit's own JSON that costs the most time to unpick. Three field names are deliberately not Reddit's, and those three are where a script ported from PRAW breaks:

KeyTypeNotes
titlestringThe post title as submitted.
authorstringUsername without the u/ prefix.
upvotesintReddit calls this score. Renamed here.
commentsintReddit calls this num_comments. Renamed here.
textstringReddit calls this selftext. Empty string on a link post.
urlstringAlways the reddit.com thread, never the outbound link.
link_urlstring or nullThe outbound link. null on a self post.
upvote_ratiofloat0 to 1, passed through from Reddit unchanged.
createdstringISO 8601 already parsed, alongside raw created_utc.

A post also carries id, name, subreddit, permalink, the four moderation flags over_18, stickied, locked and spoiler, and a crosspost block that is null unless the post is one. Both time fields ship together, so you can sort on the epoch and print the ISO string without converting either.

Why you can skip PRAW

PRAW is a wrapper around the official Reddit OAuth API, and almost everything it asks of you exists because OAuth asks it first. You register a Reddit app to get a client id and a client secret, you pick a grant type, you hold a refresh token, and you keep a client object alive because that is where the token lives. None of that is Python; it is the handshake, wearing a Python interface.

Redditapis replaces the whole handshake with one bearer token you paste into a header, so there is no app to register and no token to refresh. That also means there is no client object to keep alive, which is why the examples here are plain requests.get calls and why they work unchanged inside a Lambda, a cron job or a notebook. See the auth details on how to authenticate the Reddit API, the per-call cost on how much the Reddit API costs, and the longer Python walkthrough with search, comment streams and a retry wrapper on the Python SDK page. If you are here because PRAW kept sleeping on you, the PRAW rate limit workaround covers that specifically.

Paging, and why an empty cursor is not an ending

One call returns 25 posts by default and 100 at most. To go further, read after from the response and send it back as a parameter on the next request. Pass it back exactly as you received it: it is an opaque string that also carries how deep into the listing you have walked, so a hand-built value from a Reddit fullname throws that away and you get a worse answer about where you are.

The part most scripts get wrong is what happens when after comes back null. It means no next page was offered. It does not mean you have every post. Reddit stops serving a busy listing long before the subreddit runs out, and from the outside the two look identical. Paging r/all sorted by new through this API terminated at 400 items covering sixty-seven seconds of Reddit, and repeat sweeps four minutes apart terminated at 153, 384, 400 and 383 items (measured on our own pool in August 2026). A number that moves 2.6x between runs is not the end of the data. Reddit also closes any single listing near 1,000 items and simply stops issuing a cursor, which we measured at 983 on r/stocks on 20 July 2026.

So the response tells you which of those you hit rather than leaving you to guess. When there is no cursor to hand back, the same JSON carries listing_status, exhausted_reason, items_returned, final_page_items and a plain-English hint. Branch on listing_status, which has exactly three values:

listing_statusWhat happenedWhat to do
completeReddit ran out of items inside the first page it was filling.Stop. Nothing is missing from this listing.
truncatedYou reached Reddit's per-listing ceiling, near 1,000 items.Widen. Change the sort, the timeframe or the query.
unknownReddit stopped serving and we cannot tell an end from a cut-off.Treat the answer as partial. Read the hint field.
cursor = None
seen = 0

while True:
    params = {"subreddit": "webdev", "sort": "new", "limit": 100}
    if cursor:
        params["after"] = cursor

    page = requests.get(
        "https://api.redditapis.com/api/reddit/posts",
        params=params,
        headers={"Authorization": "Bearer YOUR_TOKEN"},
        timeout=30,
    ).json()

    for post in page["posts"]:
        print(post["title"])
    seen += len(page["posts"])

    cursor = page["after"]
    if not cursor:
        # No next page was offered. That is NOT the same as having every post.
        status = page["listing_status"]
        print(f"{seen} posts, listing_status={status}")
        if status != "complete":
            print(page["hint"])
        break

When the answer is truncated or unknown, paging harder does not help, because the cursor you would need is the thing Reddit withheld. Widen instead. Each sort, each timeframe and each search query is a separate listing with its own budget, so the same subreddit read as sort=top&t=year reaches items that sort=new can no longer hand you. Each request in the loop is billed the same $0.002, so ten pages of 100 posts is two cents, with no separate pagination charge and no minimum.

Three ways a Python script quietly wastes credits

Every one of these returns HTTP 200. That is what makes them expensive: nothing raises, the script finishes, and the bill is the only place the mistake shows up.

Building the query string by hand. One caller concatenated two parameters into one and sent sort=limit=50 three hundred and fifty-one times. Reddit ignored the value, so they silently got the wrong sort and the default limit of 25 instead of the 50 rows they asked for, and they were billed for all 351 calls with nothing to tell them. The API now rejects an unrecognised sort with a 400 that names the allowed values, and passing params a dict makes the slip impossible, because requests escapes each value separately.

Sending a timeframe that does not apply. The t parameter accepts hour, day, week, month, year and all, and it is only forwarded when sort is top or controversial. Sending sort=new&t=week is a paid call for the newest posts regardless of the week you asked for. The valid sorts are new, hot, top, rising, controversial and best.

Leaving limit unset in a loop. The default is 25 and the ceiling is 100. A page costs $0.002 whichever you choose, so an omitted limit makes a thousand-post crawl cost four times what it needs to. An out-of-range value is a 400 rather than a silent clamp, which is the one failure in this section you find out about immediately.

A typo in the path is the one mistake that is free. A request matching no route is never billed, and if a broken client sends enough of them on one key the API answers 429 with a Retry-After header and charges nothing for that either. To watch spend without spending, read the X-Credits-Remaining header that rides on every authenticated response, so a long run can stop itself before it runs the balance to zero.

Frequently asked

How do I use the Reddit API in Python?

Use the requests library to send a GET to api.redditapis.com with your bearer token in the Authorization header. Parse the clean JSON with response.json(). There is no PRAW to install and no OAuth handshake to manage.

Do I need PRAW?

No. PRAW wraps the official Reddit OAuth API, so it needs a registered Reddit app, a client id and secret, and a token it refreshes for you. Redditapis is a plain REST endpoint with one bearer token, so the requests library is enough and there is nothing to register.

What does a Python read cost?

A GET read is $0.002 per call. You start with $0.50 in free credits, about 250 reads, so you can build and test your script before adding a card.

How do I paginate results?

Send the after value from one response back as the after parameter on the next request. When after comes back null, read listing_status in the same response: only the value complete means the listing held nothing more.

What fields does a post carry?

Each row in the posts array carries title, author, upvotes, comments, text, url, link_url, upvote_ratio, over_18, stickied, locked, spoiler and created, plus the raw created_utc. Reddit's score, num_comments and selftext are renamed to upvotes, comments and text.

Am I charged if I call the wrong path?

No. A request that matches no route is not billed. Send enough of them on one key and the API answers 429 with a Retry-After header instead, and that response is not billed either.

Run this in Python in minutes

Grab a bearer token, drop it into the example, and start with $0.50 in free credits. No PRAW, no OAuth.

Get your Reddit API key