# Medium Scraper - Articles, Tags, Authors & Full Text (`scrapewise/medium-scraper`) Actor

Scrape Medium without login: search articles, tag feeds (latest, hot, top week, month, year), author and publication archives, or article links. Claps, responses, reading time, tags, author followers, plus optional full text in plain text and Markdown.

- **URL**: https://apify.com/scrapewise/medium-scraper.md
- **Developed by:** [Scrapewise Data](https://apify.com/scrapewise) (community)
- **Categories:** News, Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.70 / 1,000 article delivereds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Medium Scraper

Medium Scraper turns **public Medium articles** into clean JSON, CSV or Excel, **without login or cookies**. Search any keyword, read tag feeds (latest, hot, top this week, month, year or all time), pull the archive of an author or a publication, or paste article links. Every article comes with title, subtitle, author and follower count, publication, date, claps, responses, reading time, word count, tags and the member-only flag, plus the **full text in plain text and Markdown** when you want it.

**US$ 2.00 per 1,000 articles, plus US$ 0.80 per 1,000 when you add the full text. No monthly fee, no start fee. Error rows are free.**

### At a glance

- **Price per 1,000 articles:** US$ 2.00 (US$ 2.80 with full text; down to US$ 1.70 on Gold and above)
- **Inputs:** Keyword search, tags x 6 sort orders, authors, publications, article links
- **Full text:** Plain text and Markdown; member-only articles get Medium's public preview, flagged
- **Date filter:** `publishedAfter` as a date or "last N days"
- **Login, cookies:** None
- **Error rows:** Free, with an `errorCode`

### One real row

Keyword `rust async`, full text on (collected on 2026-09-30, text trimmed):

```json
{
  "type": "article",
  "id": "43e4ecf62d2e",
  "title": "Rust Async/Await Explained What Really Happens Behind the Scenes",
  "url": "https://medium.com/@highsongs2020/rust-async-await-explained-what-really-happens-behind-the-scenes-43e4ecf62d2e",
  "publishedAt": "2026-09-22T07:32:01Z",
  "claps": 2,
  "responses": 0,
  "readingTimeMinutes": 4.2,
  "wordCount": 1070,
  "isMemberOnly": true,
  "tags": ["rust", "programming", "software-development", "software-engineering", "backend-development"],
  "authorName": "SOVANNARO",
  "authorFollowers": 52,
  "contentMarkdown": "## Rust Async/Await Explained What Really Happens Behind the Scenes\n\n### Go beyond writing .await ...",
  "contentIsPreview": true,
  "source": "search:rust async",
  "rank": 1
}
```

### What you can do with it

- **Content research and SEO:** see what gets claps and responses in your topic this week, month or year, and which angles are already covered.
- **Author and publication tracking:** schedule a run on the writers and publications you follow and get every new article as a row.
- **Datasets for AI:** full text in Markdown, ready for RAG, summarization or training pipelines (open articles in full, member-only as preview).
- **Market signals:** track how often a product, library or company shows up in Medium articles over time.

### Input

| Field | What it does |
|---|---|
| `searchQueries` | Keywords. Medium's relevance order, 10 per page, up to `maxItems`. |
| `tags` | Tag slugs or links. Up to 25 articles per sort order. |
| `tagSort` | `latest`, `hot`, `top_week`, `top_month`, `top_year`, `top_all_time` (several allowed). |
| `authors` | `@username`, profile link or `name.medium.com`. Paged archive. |
| `publications` | Slug, custom domain or link. Paged archive. |
| `articleUrls` | Article links or 12-character ids. |
| `maxItems` | Max articles per keyword, tag sort, author or publication. Default 50. |
| `publishedAfter` | `YYYY-MM-DD` or a number of days. |
| `includeContent` | Add `content` and `contentMarkdown`. |
| `excludeMemberOnly` | Skip articles behind the paywall. |
| `onlyNew` | Only articles not delivered in earlier runs, for schedules. Default off. |

```json
{ "tags": ["startup", "product-management"], "tagSort": ["top_week", "top_month"], "maxItems": 25 }
```

```json
{ "publications": ["pub.towardsai.net"], "publishedAfter": "30", "includeContent": true, "maxItems": 200 }
```

### Schedule it daily: only new articles

Turn on `onlyNew`, save the input as a Task and attach a Schedule. The Actor remembers which
articles of each tag, author, publication and keyword it already delivered (in a Key-Value Store
named `medium-scraper-only-new`, in your own Apify account), so every run delivers, and charges,
only the articles it has not given you before; they do not count in `maxItems` either. Authors
and publications stop paging at the first page you already have. A day with nothing new ends
green with the status "No new articles since the last run" and costs nothing for results. Point
it at a tag with `tagSort: latest` or at the publications you follow, connect Slack, Google Sheets
or a webhook, and new articles land where you read them. Delete the store to start over.

```json
{ "tags": ["artificial-intelligence"], "tagSort": ["latest"], "publications": ["pub.towardsai.net"], "maxItems": 25, "onlyNew": true }
```

### Output fields

| Field | What it is |
|---|---|
| `id`, `title`, `subtitle`, `url` | The article |
| `publishedAt`, `updatedAt` | ISO 8601 UTC |
| `claps`, `responses` | Engagement as Medium shows it |
| `readingTimeMinutes`, `wordCount` | Length |
| `isMemberOnly` | Behind the Medium paywall |
| `tags`, `imageUrl` | Tag slugs and preview image |
| `authorName`, `authorUsername`, `authorUrl`, `authorFollowers`, `authorBio` | The writer's public profile |
| `publicationName`, `publicationSlug` | The publication, when there is one |
| `content`, `contentMarkdown`, `contentIsPreview` | Body text (with `includeContent`); `contentIsPreview` is true for member-only articles |
| `source`, `rank` | Which input produced the row, and its position in search or tag results |

#### Error codes (never charged)

| errorCode | Meaning |
|---|---|
| `NO_RESULTS` | Nothing for that keyword, tag, author or publication in the date window |
| `NOT_FOUND` | No such author or publication, or the article was deleted or made private |
| `INVALID_URL` | The line is not a Medium article link or id |
| `INVALID_INPUT` | A filter has an unknown value |
| `BLOCKED`, `NOT_REACHED`, `UNEXPECTED` | Medium did not answer, the run timeout arrived, or something unforeseen broke; only that input is lost |

### Pricing

| Event | Per 1,000 |
|---|---|
| Article delivered | US$ 2.00 |
| Article text (only with `includeContent`) | US$ 0.80 |

Duplicates, articles outside the date filter and error rows are free. There is no Apify compute bill on top.

### Limitations

- **Member-only articles:** logged-out readers see a preview, and so does this Actor. The row says so in `contentIsPreview`.
- **Tag feeds stop at 25 per sort order.** Medium does not page tag feeds for logged-out readers; combine sort orders, related tags and keyword search for more.
- **Author and publication archives are not strictly by date** (Medium mixes pinned and featured posts), so `publishedAfter` skips old ones instead of stopping at the first.
- This Actor reads public pages only and respects the site's terms.

### FAQ

**Do I need a Medium account or cookies?**
No. The Actor reads public Medium pages without login or cookies.

**What does a run cost?**
US$ 2.00 per 1,000 articles, plus US$ 0.80 per 1,000 when you turn on `includeContent` for the full text. Duplicates, articles outside the date filter and error rows are free, and there is no Apify compute bill on top.

**Can I get the full text of member-only articles?**
No. Logged-out readers see a preview, and so does this Actor; those rows have `contentIsPreview` set to true. Turn on `excludeMemberOnly` to skip paywalled articles, and they are not charged.

**Why does a tag return only about 25 articles?**
Medium does not page tag feeds for logged-out readers. Pick several orders in `tagSort` (each one returns up to 25, and an article in more than one feed is charged once), or add related tags and `searchQueries`.

**How do I get only recent articles?**
Set `publishedAfter` to a date (`YYYY-MM-DD`) or a number of days back (`30` = the last 30 days). Older articles are skipped and not charged.

**Can I schedule it and get only new articles?**
Yes. Turn on `onlyNew`, save the input as a Task and attach a Schedule. Articles delivered in earlier runs are skipped, not charged and not counted in `maxItems`, and a day with nothing new costs nothing for results.

### Changelog

- **2026-10-17:** FAQ in this page.
- **2026-10-04:** `onlyNew` option for scheduled runs: articles delivered in earlier runs are skipped, not charged and not counted in `maxItems`.
- **2026-09-30, 0.1:** first release. Keyword search, tag feeds in six sort orders, author and publication archives, article links, full text in text and Markdown, date filter, free error rows.

# Actor input Schema

## `searchQueries` (type: `array`):

Keywords to search on Medium, one per line (for example "rust async" or "product management"). Results come in Medium's relevance order and stop at 'Max articles per input'. Each row keeps the keyword in its source column. Also accepts 'queries', 'keywords' or 'query'. Leave every list empty to run the example tag 'programming'.

## `tags` (type: `array`):

Medium tags as slugs or tag links, one per line (for example programming, data-science or https://medium.com/tag/startup). Spaces become dashes. Each tag returns up to 25 articles for every sort order chosen below. Also accepts 'tag'.

## `tagSort` (type: `array`):

Which tag feeds to read: Latest (default), Hot, Top this week, Top this month, Top this year or Top of all time. Medium caps each feed at about 25 articles, so pick several orders to get more articles per tag; articles that appear in more than one feed are delivered and charged once. Used only with Tags.

## `authors` (type: `array`):

Medium writers, one per line, as a username, profile link or subdomain (for example @karpathy, https://medium.com/@someone or someone.medium.com). Returns the author's articles from newest to oldest, up to 'Max articles per input'. Also accepts 'users', 'usernames' or 'profileUrls'.

## `publications` (type: `array`):

Medium publications, one per line, as a slug, custom domain or link (for example towards-artificial-intelligence, pub.towardsai.net or https://medium.com/better-programming). Returns the publication's latest articles, up to 'Max articles per input'. Also accepts 'publicationUrls'.

## `articleUrls` (type: `array`):

Specific Medium articles, one per line, as a link or the 12-character id at the end of the link (for example https://medium.com/@user/title-1a2b3c4d5e6f or 1a2b3c4d5e6f). Custom-domain articles work too. Each link returns one row. Also accepts 'startUrls', 'urls' or 'postUrls'.

## `maxItems` (type: `integer`):

Maximum articles for each keyword, tag sort order, author or publication (default 50). With 2 keywords and 50 here a run delivers up to 100 articles. You pay per article delivered, so this caps the cost of each input. Article links are always read in full.

## `publishedAfter` (type: `string`):

Only articles published on or after this date. Use YYYY-MM-DD (for example 2026-01-01) or a number of days back (30 = the last 30 days). Older articles are skipped and not charged. Leave empty for no date filter.

## `includeContent` (type: `boolean`):

Adds the article body as plain text ('content') and Markdown ('markdown'). Member-only articles come with the preview Medium shows to logged-out readers and are flagged contentIsPreview. Off by default; when on, each article with a body is charged an extra 'Article text' event.

## `excludeMemberOnly` (type: `boolean`):

Leaves out articles behind the Medium paywall (member-only), so every row has a fully readable article. Skipped articles are not charged. Off by default.

## `onlyNew` (type: `boolean`):

For scheduled Tasks: remembers which articles each keyword, tag sort, author, publication and link list already delivered (in a named key-value store in your account, 'medium-scraper-only-new') and skips them on later runs. Skipped articles are not delivered, not charged and do not count toward Max articles. The first run delivers normally. Delete that store to start over. Also accepts 'onlyNewArticles'.

## `proxyConfiguration` (type: `object`):

Apify Proxy, on by default (datacenter). Medium's public pages work with it; keep the default unless you have your own proxy.

## Actor input object example

```json
{
  "searchQueries": [
    "rust async"
  ],
  "tagSort": [
    "latest"
  ],
  "maxItems": 50,
  "includeContent": false,
  "excludeMemberOnly": false,
  "onlyNew": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `resultsCsv` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchQueries": [
        "rust async"
    ],
    "maxItems": 50,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapewise/medium-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "searchQueries": ["rust async"],
    "maxItems": 50,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("scrapewise/medium-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchQueries": [
    "rust async"
  ],
  "maxItems": 50,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call scrapewise/medium-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapewise/medium-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/mg62otiphLIFn8NGM/builds/sUMN3bLIY8DimHy4I/openapi.json
