Reddit closes its public API in March 2027. New accounts here start with $0.50 free.See the timeline ↗

Reddit API documentation

The base URL is https://api.apixstar.com. Every endpoint is a GET that returns JSON. The Reddit object you want goes in the path, and query parameters only filter or page the result.

Authentication

Send an API key in the Authorization header of every request. Keys start with xstar_. You create them in the console, and each one is shown once.

Authorization: Bearer xstar_...

A key in the query string is not accepted. A revoked or unknown key returns INVALID_API_KEY and the request never reaches Reddit.

Endpoints

Name the community, user, post or comment in the path. Do not pass a Reddit URL or permalink as a parameter.

EndpointReturns
GET /reddit/v1/r/{community}Community listing, hot by default
GET /reddit/v1/r/{community}/{sort}Listing sorted by new, hot, top, rising, or controversial
GET /reddit/v1/r/{community}/comments/{id}Post thread with comments
GET /reddit/v1/r/{community}/searchSearch inside one community
GET /reddit/v1/u/{username}User profile
GET /reddit/v1/u/{username}/submittedPosts submitted by a user
GET /reddit/v1/posts/{id}One post by id, with comments
GET /reddit/v1/comments/{id}One comment by id
GET /reddit/v1/searchSearch across Reddit

Post and comment ids work with or without their t3_ and t1_ prefixes.

Responses and billing

A successful response has ok: true, the result in data and request details in meta. Read meta.billing for what the request cost and the balance that remains.

Reading a post with its comments, a comment or a user profile costs $0.0007 per request. A listing or search costs $0.0007 per 10 results returned, rounded up per page, so a full page of 100 results costs $0.007. A search that includes comments counts posts and comments together.

Empty pages and failed requests are free. New accounts start with $0.50. The pricing page lists the price of every endpoint.

You can send an optional X-Request-ID header. It is scoped to your account. Sending the same id again returns HTTP 409 without a second upstream request or charge, so a retry cannot bill you twice. Use a new id for each new request.

How to read the examples

Each endpoint below has a complete JSON example you can expand. It is based on a successful live request, with text, arrays and balances shortened or replaced by illustrative values. Each example shows one result.

  • data.posts is an array for listings and searches. A read returns one object in data.post, data.comment or data.user.
  • id is the bare id and fullname adds t3_ for a post or t1_ for a comment. Either form works in the read endpoints.
  • createdAt is Unix time in seconds. meta.ts is Unix time in milliseconds.
  • data.nextCursor is optional. When it is present, send it back as cursor without interpreting it. Stop when it is absent or null.
  • A post's flair may be null. commentCount is the total on the post, not the number of comments in this response. locked and archived are optional booleans. Optional media entries have a type (image or video) and a url, and videos may add posterUrl and hlsUrl.
  • meta.billing.resultCount counts the posts and comments returned by a listing, or one resource for a read. billingUnits is the number of units charged. unitPrice, charged and balanceRemaining are USD strings with four decimal places. pricingId and version identify the pricing rule.

Communities

GET /reddit/v1/r/{community} lists posts in a community, hot first. Add /new, /hot, /top, /rising or /controversial to choose the order. The top listing covers the past week.

QueryRequiredMeaning
limitnoPage size, 1 to 100. The default is 10.
cursornoThe nextCursor value from the previous page. Pass it unchanged.
minScorenoLeave out posts below this score.
maxAgeHoursnoLeave out posts older than this many hours.
excludeAuthorsnoAuthor names to leave out, separated by commas.

Filters apply to each page after it is fetched, so a page can hold fewer than limit results, or none, and still return nextCursor. Pass that value back unchanged to continue with the same filters. Stop when nextCursor is absent, not when a page is short or empty.

curl "https://api.apixstar.com/reddit/v1/r/typescript/new?limit=10" \
  -H "Authorization: Bearer YOUR_API_KEY"
GET /reddit/v1/r/{community} response JSON

HTTP 200. The values are illustrative. Field names and structure match a tested response.

{
  "ok": true,
  "data": {
    "posts": [
      {
        "id": "1wxjay5",
        "fullname": "t3_1wxjay5",
        "title": "Showcase Thread",
        "body": "Example content.",
        "author": "AutoModerator",
        "community": "Python",
        "url": "https://www.reddit.com/r/Python/comments/1wxjay5/showcase_thread/",
        "permalink": "https://www.reddit.com/r/Python/comments/1wxjay5/showcase_thread/",
        "score": 17,
        "upvoteRatio": 0.96,
        "commentCount": 39,
        "createdAt": 1791129917,
        "isRemoved": false,
        "isSelf": true,
        "stickied": true,
        "flair": "Showcase",
        "platform": "reddit"
      }
    ],
    "nextCursor": "t3_1wxjay5"
  },
  "meta": {
    "method": "feed",
    "path": "/reddit/v1/r/Python",
    "ts": 1791549435096,
    "billing": {
      "pricingId": "community.hot",
      "version": "usd-v2",
      "resultCount": 1,
      "billingUnits": 1,
      "unitPrice": "0.0007",
      "charged": "0.0007",
      "balanceRemaining": "9.9993",
      "currency": "USD"
    }
  }
}
GET /reddit/v1/r/{community}/{sort} response JSON

HTTP 200. The example uses new. The other sorts return the same shape, and meta.path and meta.billing.pricingId reflect the sort you chose.

{
  "ok": true,
  "data": {
    "posts": [
      {
        "id": "1x16rfy",
        "fullname": "t3_1x16rfy",
        "title": "Friday Daily Thread: r/Python Meta and Free-Talk Fridays",
        "body": "Example content.",
        "author": "AutoModerator",
        "community": "Python",
        "url": "https://www.reddit.com/r/Python/comments/1x16rfy/friday_daily_thread_rpython_meta_and_freetalk/",
        "permalink": "https://www.reddit.com/r/Python/comments/1x16rfy/friday_daily_thread_rpython_meta_and_freetalk/",
        "score": 3,
        "upvoteRatio": 1,
        "commentCount": 0,
        "createdAt": 1791504032,
        "isRemoved": false,
        "isSelf": true,
        "stickied": true,
        "flair": ":pythonLogo: Daily Thread",
        "platform": "reddit"
      }
    ],
    "nextCursor": "t3_1x16rfy"
  },
  "meta": {
    "method": "feed",
    "path": "/reddit/v1/r/Python/new",
    "ts": 1791549438295,
    "billing": {
      "pricingId": "community.new",
      "version": "usd-v2",
      "resultCount": 1,
      "billingUnits": 1,
      "unitPrice": "0.0007",
      "charged": "0.0007",
      "balanceRemaining": "9.9993",
      "currency": "USD"
    }
  }
}

Users

GET /reddit/v1/u/{username} returns a user profile. GET /reddit/v1/u/{username}/submitted lists that user's posts, newest first, and takes the paging parameters. /posts is an alias for /submitted.

QueryRequiredMeaning
limitnoPage size, 1 to 100. The default is 10.
cursornoThe nextCursor value from the previous page. Pass it unchanged.
curl "https://api.apixstar.com/reddit/v1/u/spez/submitted?limit=25" \
  -H "Authorization: Bearer YOUR_API_KEY"
GET /reddit/v1/u/{username} response JSON

HTTP 200. The profile is in data.user. avatarUrl may be null.

{
  "ok": true,
  "data": {
    "user": {
      "username": "spez",
      "totalKarma": 941030,
      "linkKarma": 184536,
      "commentKarma": 756494,
      "createdAt": 1118030400,
      "isSuspended": false,
      "avatarUrl": "https://styles.redditmedia.com/t5_3k30p/styles/profileIcon_uj015iwx9s7g1.png?width=256&height=256&crop=256:256,smart&s=aa819b0964f6e3019e769d1cc8ede7318c3869a3",
      "platform": "reddit"
    }
  },
  "meta": {
    "method": "read",
    "path": "/reddit/v1/u/spez",
    "ts": 1791549483437,
    "billing": {
      "pricingId": "read.user",
      "version": "usd-v1",
      "resultCount": 1,
      "billingUnits": 1,
      "unitPrice": "0.0007",
      "charged": "0.0007",
      "balanceRemaining": "9.9993",
      "currency": "USD"
    }
  }
}
GET /reddit/v1/u/{username}/submitted response JSON

HTTP 200. Posts are in data.posts and paging uses data.nextCursor. The /posts alias has the same shape and price, and meta.path records the path you requested.

{
  "ok": true,
  "data": {
    "posts": [
      {
        "id": "1vgbkge",
        "fullname": "t3_1vgbkge",
        "title": "Modernizing Reddit’s infrastructure with you",
        "body": "Example content.",
        "author": "spez",
        "community": "u_spez",
        "url": "https://www.reddit.com/r/u_spez/comments/1vgbkge/modernizing_reddits_infrastructure_with_you/",
        "permalink": "https://www.reddit.com/r/u_spez/comments/1vgbkge/modernizing_reddits_infrastructure_with_you/",
        "score": 349,
        "upvoteRatio": 0.84,
        "commentCount": 477,
        "createdAt": 1785945836,
        "isRemoved": false,
        "isSelf": true,
        "stickied": false,
        "flair": null,
        "platform": "reddit"
      }
    ],
    "nextCursor": "t3_1vgbkge"
  },
  "meta": {
    "method": "feed",
    "path": "/reddit/v1/u/spez/submitted",
    "ts": 1791549487156,
    "billing": {
      "pricingId": "user.feed",
      "version": "usd-v2",
      "resultCount": 1,
      "billingUnits": 1,
      "unitPrice": "0.0007",
      "charged": "0.0007",
      "balanceRemaining": "9.9993",
      "currency": "USD"
    }
  }
}

Posts and comments

GET /reddit/v1/posts/{id} returns one post with its comments. GET /reddit/v1/r/{community}/comments/{id} returns the same thread when you also know the community. GET /reddit/v1/comments/{id} returns a single comment.

QueryRequiredMeaning
includeChildrennoSet to false to read a post without its comments.
commentLimitnoMost comments to return with a post. The default is 50 and the maximum is 200.
curl "https://api.apixstar.com/reddit/v1/posts/1abc2de?commentLimit=200" \
  -H "Authorization: Bearer YOUR_API_KEY"
GET /reddit/v1/posts/{id} response JSON

HTTP 200. The post is in data.post. comments holds the comments returned with it, not the whole thread, and is left out when includeChildren=false. The example shows one comment.

{
  "ok": true,
  "data": {
    "post": {
      "id": "1wxjay5",
      "fullname": "t3_1wxjay5",
      "title": "Showcase Thread",
      "body": "Example content.",
      "author": "AutoModerator",
      "community": "Python",
      "url": "https://www.reddit.com/r/Python/comments/1wxjay5/showcase_thread/",
      "permalink": "https://www.reddit.com/r/Python/comments/1wxjay5/showcase_thread/",
      "score": 18,
      "upvoteRatio": 1,
      "commentCount": 39,
      "createdAt": 1791129917,
      "isRemoved": false,
      "isSelf": true,
      "stickied": true,
      "flair": "Showcase",
      "platform": "reddit",
      "comments": [
        {
          "id": "pdtyaj5",
          "fullname": "t1_pdtyaj5",
          "body": "Example content.",
          "author": "fykup",
          "community": "Python",
          "parentId": "t3_1wxjay5",
          "postId": "t3_1wxjay5",
          "score": 7,
          "createdAt": 1791130136,
          "permalink": "https://www.reddit.com/r/Python/comments/1wxjay5/showcase_thread/pdtyaj5/",
          "platform": "reddit"
        }
      ]
    }
  },
  "meta": {
    "method": "read",
    "path": "/reddit/v1/posts/1wxjay5",
    "ts": 1791549474887,
    "billing": {
      "pricingId": "read.post",
      "version": "usd-v1",
      "resultCount": 1,
      "billingUnits": 1,
      "unitPrice": "0.0007",
      "charged": "0.0007",
      "balanceRemaining": "9.9993",
      "currency": "USD"
    }
  }
}
GET /reddit/v1/r/{community}/comments/{id} response JSON

HTTP 200. The same post shape is returned in data.post, with the optional comments. This endpoint bills as read.thread.

{
  "ok": true,
  "data": {
    "post": {
      "id": "1wxjay5",
      "fullname": "t3_1wxjay5",
      "title": "Showcase Thread",
      "body": "Example content.",
      "author": "AutoModerator",
      "community": "Python",
      "url": "https://www.reddit.com/r/Python/comments/1wxjay5/showcase_thread/",
      "permalink": "https://www.reddit.com/r/Python/comments/1wxjay5/showcase_thread/",
      "score": 16,
      "upvoteRatio": 0.92,
      "commentCount": 39,
      "createdAt": 1791129917,
      "isRemoved": false,
      "isSelf": true,
      "stickied": true,
      "flair": "Showcase",
      "platform": "reddit",
      "comments": [
        {
          "id": "pdtyaj5",
          "fullname": "t1_pdtyaj5",
          "body": "Example content.",
          "author": "fykup",
          "community": "Python",
          "parentId": "t3_1wxjay5",
          "postId": "t3_1wxjay5",
          "score": 7,
          "createdAt": 1791130136,
          "permalink": "https://www.reddit.com/r/Python/comments/1wxjay5/showcase_thread/pdtyaj5/",
          "platform": "reddit"
        }
      ]
    }
  },
  "meta": {
    "method": "read",
    "path": "/reddit/v1/r/Python/comments/1wxjay5",
    "ts": 1791549467917,
    "billing": {
      "pricingId": "read.thread",
      "version": "usd-v1",
      "resultCount": 1,
      "billingUnits": 1,
      "unitPrice": "0.0007",
      "charged": "0.0007",
      "balanceRemaining": "9.9993",
      "currency": "USD"
    }
  }
}
GET /reddit/v1/comments/{id} response JSON

HTTP 200. The comment is in data.comment. postId is the fullname of its post. parentId names either the post (t3_) or a parent comment (t1_).

{
  "ok": true,
  "data": {
    "comment": {
      "id": "pdtyaj5",
      "fullname": "t1_pdtyaj5",
      "body": "Example content.",
      "author": "fykup",
      "community": "Python",
      "parentId": "t3_1wxjay5",
      "postId": "t3_1wxjay5",
      "score": 6,
      "createdAt": 1791130136,
      "permalink": "https://www.reddit.com/r/Python/comments/1wxjay5/showcase_thread/pdtyaj5/",
      "platform": "reddit"
    }
  },
  "meta": {
    "method": "read",
    "path": "/reddit/v1/comments/pdtyaj5",
    "ts": 1791549477235,
    "billing": {
      "pricingId": "read.comment",
      "version": "usd-v1",
      "resultCount": 1,
      "billingUnits": 1,
      "unitPrice": "0.0007",
      "charged": "0.0007",
      "balanceRemaining": "9.9993",
      "currency": "USD"
    }
  }
}

GET /reddit/v1/search searches all of Reddit. GET /reddit/v1/r/{community}/search searches inside one community. Both take the same parameters.

QueryRequiredMeaning
qyesThe search text.
sortnorelevance, hot, top, new or comments. The default is relevance.
timeRangenohour, day, week, month, year or all. The default is week, so use all to search all time. t is accepted as an alias.
limitnoPage size, 1 to 100. The default is 10.
cursornoThe nextCursor value from the previous page. Pass it unchanged.
includeCommentsnoSet to true to search comments as well as posts. The default is false.

To page through results, keep the query, community, sort, time range and includeComments the same and pass each nextCursor back as cursor. Stop when a response has no nextCursor.

curl "https://api.apixstar.com/reddit/v1/r/typescript/search?q=bun&limit=10" \
  -H "Authorization: Bearer YOUR_API_KEY"

When includeComments=true

Both search endpoints then return a data.posts array and a data.comments array. Together they hold at most limit items, and either can be empty. Comments have the same fields as data.comment above. data.partial is a boolean. data.errors is an array that contains posts_unavailable or comments_unavailable when that half failed, and is empty otherwise.

A partial response can still have ok: true, and it is billed for the results it returned. If both halves fail, the request returns an error. Pass data.nextCursor back unchanged to continue. It can hold separate positions for posts and comments.

Rate limits

Every account can send 30 requests per second, shared across all of its API keys and regardless of top-up amount. The counter resets each second. Responses carry X-RateLimit-Limit and X-RateLimit-Remaining. Going over the limit returns RATE_LIMITED with Retry-After: 1.

Errors

A failed request has ok: false and an error.code. Failed requests are never charged.

CodeHTTPMeaning
INVALID_API_KEY401The Authorization: Bearer header is missing, the key has an unsupported prefix, or the key is unknown or revoked.
ACCOUNT_FROZEN403The account is frozen or inactive. Contact support to restore access.
INSUFFICIENT_BALANCE402The balance cannot cover the largest possible charge for this request. Reddit is not called.
RATE_LIMITED429The account exceeded 30 requests per second, shared across its API keys. Wait for the time in Retry-After.
INVALID_PATH400The path is not a supported resource, or a search is missing q.
INVALID_LIMIT400For listings and searches, limit must be a single integer from 1 to 100. Empty, repeated, fractional and out-of-range values are rejected.
INVALID_REQUEST400An upstream parameter validation failed. Check error.details.issues when present and correct the indicated parameters.
IDEMPOTENCY_CONFLICT409An X-Request-ID was reused with different parameters or a different API key in the same account. Use a new ID for a different request.
REQUEST_IN_PROGRESS409A request with the same X-Request-ID still has an active reservation. This duplicate does not call Reddit or charge again. Wait for the original request to finish.
REQUEST_ALREADY_PROCESSED409The X-Request-ID belongs to a completed, failed or expired request. The previous response is not replayed, and this duplicate does not call Reddit or charge again. After a confirmed failure, use a new ID to retry.
RESERVATION_NOT_ACTIVE409The billing reservation expired or was released before settlement. This attempt is not charged. Retry as a new request with a new X-Request-ID.
ORIGIN_RESULT_INVALID502The upstream response has an invalid result shape or exceeds the requested result limit. The request is not charged. Retry later; contact support if it persists.
PRICING_UNAVAILABLE503Pricing for this endpoint is temporarily unavailable. Reddit is not called. Retry later.
REDDIT_RATE_LIMITED429Reddit answered with a rate limit. Wait for Retry-After when present before retrying.
REDDIT_TIMEOUT504Reddit did not answer in time. Retry later; use a new X-Request-ID after this confirmed failure.
REDDIT_HTTP_ERROR502 or upstream statusReddit returned an error or a blocked page. The upstream HTTP status is forwarded when available.
REDDIT_UNAVAILABLE502 or 503The upstream reader is down, busy or cooling down. Retry later; use a new X-Request-ID after this confirmed failure.
INTERNAL_ERROR500An unexpected server error occurred. Contact support if it persists. If the original outcome is uncertain, do not automatically use a new request ID: it can create another billable request.

Error response example

HTTP 400 for a listing limit that is out of range. An error response has ok: false and error, and may add error.details. Do not expect data or meta.billing on a failure.

{
  "ok": false,
  "error": {
    "code": "INVALID_LIMIT",
    "message": "limit must be one integer from 1 to 100"
  }
}

Want to try a request before writing code? The playground runs any endpoint with one of your keys. For agents, see the MCP server.

Make your first request

Sign in, create a key and call an endpoint. You start with $0.50 of free credit.

$0.50 free credit · Google sign-in · No card required