# Medium Scraper — Articles, Authors, Tags & Search (`nice_dev/medium-articles-scraper`) Actor

Scrape Medium articles by tag, keyword, author, publication or link: claps, responses, reading time, tags and author. Full text, comments and profiles as options. Walk any tag by date. No login. Export JSON, CSV, Excel.

- **URL**: https://apify.com/nice_dev/medium-articles-scraper.md
- **Developed by:** [Nice Dev](https://apify.com/nice_dev) (community)
- **Categories:** News, Social media, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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

### 📰 What is Medium Scraper?

**Medium Scraper** extracts **articles from [Medium](https://medium.com)**: **title, author, publication, claps, responses, reading time, tags, publication date** and, as an option, the **full text in markdown**. It also lists **authors, publications and tags** (from a search, a followers list or a profile) and the **responses** of each article. It works as a **Medium API alternative**: run it on a schedule, call it from your code, or plug it into Make, Zapier or n8n.

Type a **tag** (`python`), a **keyword**, an **author** or a **publication**, or paste any Medium link, click **Start**, and download the results in JSON, CSV or Excel. No login, nothing to set up, and it is **fast: about 100 articles in 6 seconds**.&#x20;

### 📋 What data can you extract from Medium?

One row per article, response, author, publication or tag (the `type` column says which), 93 fields:

| Category | What you get |
| --- | --- |
| 📰 **Article** | title, subtitle, link, preview image, tags, topics, language — `10 MCP Servers That Do Things I Didn't Know MCP Could Do` |
| 👏 **Engagement** | claps, people who clapped, responses, claps per 1,000 followers, the most highlighted passage, up to 25 clappers |
| ✍️ **Author** | name, username, followers, bio, X handle, membership — `Arham Ali, 263 followers` |
| 🗞️ **Publication** | name, link, followers, own domain, editors |
| 🕒 **Dates** | first publication, last update, "fresh" flag (less than 48 hours old) — what the date filters read |
| 📝 **Full text** (option) | the article as markdown, plain text or HTML, its headings, links, images and code languages |
| 💬 **Responses** (option) | each comment as a row: text, claps, author, date |
| 📊 **Author and publication profiles** (option) | last story date, stories in the last 30 / 90 days, median claps and responses, best story, top tags, share of member-only stories |
| 📇 **Bio contacts** (option) | e-mail addresses and phone numbers written in an author's bio or a publication's description |

Every field, with an example, is listed in the **Output** section below.

### ✅ Why use Medium Scraper?

- 🚀 **Fast**: about 100 articles in 6 seconds, with their claps, responses and author.
- 🧭 **Every way into Medium in one Actor**: tags, keywords, authors, publications, followers, article links, or any Medium link pasted as it is. Mix them in one run.
- 📅 **Any tag, any month**: walk a tag's archive back to 2000, jump straight to the months you ask for, or take Medium's own rankings (most read of each month, hot, top of the week / month / year / all time).
- 👥 **People and publications too**: search authors, publications or tags by keyword, or list the followers of an author and the accounts they follow.
- 🔔 **Monitoring built in**: tick **New articles only**, schedule the Actor, and each run returns (and charges) only the articles it has never delivered before.
- 🧩 **Pay for what you use**: the full text, responses, profiles, comparisons and contacts are options, off by default.
- 🔌 API, scheduling, monitoring, integrations (Make, Zapier, n8n, Google Sheets…), proxy rotation and JSON/CSV/Excel export via the Apify platform.

### 🚀 How to scrape Medium

1. Create a free Apify account.
2. Open **Medium Scraper** and type one or more **Tags** (e.g. `artificial-intelligence`), a **Search keyword**, **Authors** or **Publications**.
3. Or paste any Medium links into **Medium links**: a tag page, a search, a profile, a publication or a single article.
4. Set **Max results (total)** (100 by default, 0 = no limit) — and **Max results per source** when you give several sources — then click **Start**.
5. Download the dataset in JSON, CSV, Excel or via API.

### 💰 How much does it cost to scrape Medium?

This Actor uses **pay per event** pricing: a price per result saved (an article, or an author, publication or tag row of a search or a followers list), plus a small fee per run start. The full text, responses, author and publication profiles, the comparison with the author's usual claps and the bio contacts are options, off by default, each charged by its own event: leave them unticked to pay for the articles alone. See the **Pricing** tab for every amount. Articles that a filter drops are not saved and not charged. Platform usage (compute, proxy) is included in the price.

### ⚙️ Input

```json
{
    "tags": ["artificial-intelligence"],
    "maxItems": 200
}
```

A tag month by month, most read first, only recent articles, only the ones not delivered before:

```json
{
    "tags": ["python", "data-science"],
    "tagSort": "mostRead",
    "postedAfter": "2026-01-01",
    "maxItemsPerQuery": 100,
    "onlyNew": true,
    "stateKey": "python-monthly"
}
```

Authors, publications and a search of people, with the full text and author profiles:

```json
{
    "authors": ["karpathy"],
    "publications": ["netflix-techblog"],
    "query": "web scraping",
    "searchType": "people",
    "includeContent": true,
    "includeAuthorProfiles": true,
    "maxItems": 300
}
```

Or with your own links:

```json
{
    "startUrls": [
        { "url": "https://medium.com/tag/python/archive/2024/03" },
        { "url": "https://medium.com/@karpathy" },
        { "url": "https://medium.com/p/a64152b37c35" }
    ],
    "maxItems": 500
}
```

| Field | Notes |
| --- | --- |
| `startUrls` | Any Medium link: a tag (`https://medium.com/tag/python`), a search (`https://medium.com/search?q=rust`), a profile (`https://medium.com/@karpathy`; the followers and following pages of a profile list the people, like the fields below), a publication (`https://medium.com/netflix-techblog`) or an article. An archive link of a tag or a publication (a year, a month or a day: `https://medium.com/tag/python/archive/2024/03`, `https://medium.com/netflix-techblog/archive/2024/06/19`) reads only the stories published in that period, crossed with `postedAfter` / `postedBefore` (nothing in common: no request, one warning). A date that does not exist (a 13th month, a 31st of April) is refused at input. |
| `query`, `searchQueries` | Keywords searched on Medium (`web scraping`); `searchQueries` adds more, one search each. Medium gives up to about 1,000 results per keyword. |
| `searchType` | What the keywords look for: `articles`, `people` (author rows), `publications` or `tags`. |
| `tags` | Tag slugs or names (`python`, `data-science`, `Machine Learning`). `C++`, `C#`, `.NET` and `F#` are read as the tags `cpp`, `csharp`, `dotnet` and `fsharp`. |
| `tagSort` | `latest` (the whole archive, newest first), `mostRead` (the most read of each month, 20 per month), or Medium's rankings `hot`, `topWeek`, `topMonth`, `topYear`, `topAllTime` (about 100 per tag). |
| `authors` | Usernames (`karpathy`, `@karpathy`) or profile links: all their stories, newest first. |
| `publications` | Publication slugs (`netflix-techblog`) or own domains (`levelup.gitconnected.com`): their whole archive, newest first. |
| `followersOf`, `followingOf` | Usernames (`karpathy`): one author row per follower, or per account they follow. |
| `articleUrls` | Direct article links (`https://medium.com/p/a64152b37c35`): one row per article. |
| `maxItems`, `maxItemsPerQuery`, `maxFollowersPerUser` | Results for the whole run (`0` = unlimited), per source (tag, keyword, author, publication, link), and per user of a followers list. Responses and the profile rows of the options are not counted in `maxItems`. |
| `includeContent`, `includeHtml` | Paid option: the full text as markdown and plain text (and HTML), with its headings, links and images. Member-only stories give Medium's public preview only, not charged as text. |
| `includeResponses`, `maxResponsesPerArticle` | Paid option: the responses of each article as rows of their own, up to 25 per article by default. |
| `includeAuthorProfiles`, `includePublicationProfiles` | Paid options: one profile row per author / publication found, with the figures of their latest 25 stories. |
| `compareWithAuthor` | Paid option: the author's median claps and how each article compares to it. |
| `includeBioContacts` | Paid option: e-mails and phone numbers written in a bio or a description, charged only when one is found. |
| `postedAfter`, `postedBefore` | Publication date range: `2026-09-01`, or a period before now (`7 days`, `2 weeks`, `1 month`, `24 hours`). |
| `excludeKeywords` | Drop the rows whose title, subtitle or tags contain one of these words (`crypto`), case ignored: articles, and also author, publication and tag rows (their name, and a publication's tags). Responses are never dropped by it. |
| `minClaps`, `minFollowers` | Keep articles with at least this many claps, and author / publication / tag rows with at least this many followers. |
| `memberOnly` | `all`, `free` (open stories only) or `memberOnly` (paywalled stories only). |
| `language` | Language as detected by Medium: a two-letter code (`en`, `es`, `fr`) also keeps its variants (`zh` keeps `zh-hant`); a code with a script (`zh-hant`) keeps exactly that one. |
| `onlyNew`, `stateKey`, `resetState` | Monitoring: only the articles never delivered under this memory key (`python-daily`); `resetState` forgets the memory. |
| Advanced | `proxyConfiguration` (Apify proxy by default, included in the price; the residential proxy is not available), `maxConcurrency`, `maxRequestsPerMinute`, `minRequestIntervalMs`, `maxRequestRetries`, `debugLog`. |

### 📦 Output

One article row from a real run (shortened: the empty columns and the options are left out):

```json
{
    "type": "article",
    "id": "c778df176349",
    "url": "https://python.plainenglish.io/10-mcp-servers-that-do-things-i-didnt-know-mcp-could-do-c778df176349",
    "title": "10 MCP Servers That Do Things I Didn’t Know MCP Could Do",
    "subtitle": "I found some MCP servers that can give AI agents surprisingly powerful new abilities.",
    "authorName": "Arham Ali",
    "authorUsername": "iarhamali2005",
    "authorUrl": "https://medium.com/@iarhamali2005",
    "authorFollowers": 263,
    "publicationName": "Python in Plain English",
    "publicationUrl": "https://python.plainenglish.io",
    "publishedAt": "2026-10-06T05:46:51.514Z",
    "claps": 444,
    "clappers": 113,
    "clapsPerClapper": 3.93,
    "responses": 11,
    "readingTimeMinutes": 5.81,
    "wordCount": 1487,
    "isMemberOnly": true,
    "language": "en",
    "tags": ["programming", "artificial-intelligence", "mcp-server", "python", "data-science"],
    "imageUrl": "https://miro.medium.com/v2/resize:fit:1400/1*65ekls5irty-vnYA601vAw.jpeg",
    "clapsPer1kFollowers": 1688.21,
    "isFresh": false,
    "topClappers": ["szabolcshetey", "dascha958", "ai-coding"],
    "source": "tag:python",
    "rank": 1,
    "scrapedAt": "2026-10-21T15:25:14.596Z"
}
```

You can download the dataset in various formats such as JSON, HTML, CSV or Excel.

#### All 93 fields

| Fields | What you get |
| --- | --- |
| `type`, `id`, `url` | `article`, `response`, `author`, `publication` or `tag`; the Medium id (a tag's slug); its page on Medium |
| `title`, `subtitle` | the article's title as its page shows it (whole, even when Medium's API cuts a long one at 100 characters) and `subtitle` = the card extract Medium shows in lists (about 140 characters, ending with `…` when Medium shortens it); the name of an author, publication or tag |
| `authorName`, `authorUsername`, `authorId`, `authorUrl`, `authorFollowers`, `authorBio`, `authorTwitter`, `authorIsMember`, `authorImageUrl` | **Author** of an article or response: `Arham Ali`, `iarhamali2005`, `263` followers, bio, X handle, Medium member or not, photo |
| `publicationName`, `publicationSlug`, `publicationId`, `publicationUrl`, `publicationFollowers`, `publicationDomain` | **Publication** the article is in (null for a personal story): `Python in Plain English`, its followers and own domain |
| `publishedAt`, `updatedAt`, `isFresh` | **Dates**: first publication and last update (ISO 8601 UTC); `isFresh` = less than 48 hours old, claps still coming in |
| `claps`, `clappers`, `clapsPerClapper`, `responses`, `clapsPer1kFollowers` | **Engagement**: `444` claps by `113` people (`3.93` each), `11` responses, claps per 1,000 author followers |
| `highlight`, `topClappers`, `allowResponses` | the passage readers highlighted most, up to 25 usernames of people who clapped, responses open or closed |
| `readingTimeMinutes`, `wordCount`, `isMemberOnly`, `language`, `tags`, `topics` | `5.81` minutes, `1487` words, paywalled or not, `en`, tag slugs, Medium topics |
| `imageUrl`, `imageAlt`, `canonicalUrl`, `isSeries`, `license`, `isShortform`, `isPinned`, `isLimitedState`, `seoTitle`, `seoDescription` | preview image and its text, original URL when first published elsewhere, series, license, short-form, pinned on the profile, Medium's limited-distribution flag, search-engine title and description |
| `markdown`, `text`, `html`, `contentIsPreview` | **Full text** (option): markdown, plain text, simple HTML; `contentIsPreview` = Medium only shows the member-only preview |
| `links`, `images`, `codeLanguages`, `headings`, `paragraphCount`, `imageCount`, `codeBlockCount` | links and images of the text, languages of its code blocks, its section headings, and counts (with the full text) |
| `authorMedianClaps`, `vsAuthorMedian` | **Comparison** (option): the author's median claps over their latest 25 stories, and this article's claps against it (`2.4` = 2.4×) |
| `inResponseToId`, `rootArticleId` | **Response** rows: the article or response answered, and the article of the thread |
| `followers`, `following`, `bio`, `websites`, `customDomain` | **Author / publication / tag** rows: followers, people followed, bio or description, links written in it, own domain |
| `membershipTier`, `isBookAuthor`, `hasNewsletter`, `memberSince`, `pronouns`, `isSuspended`, `followingPublications` | **Author** rows: membership (`MEMBER`, `FRIEND`), book author, newsletter, member since, pronouns, suspended account, publications followed |
| `lastPostAt`, `postsLast30Days`, `postsLast90Days`, `medianClaps`, `medianResponses`, `topTags`, `bestPostTitle`, `bestPostUrl`, `bestPostClaps`, `memberOnlyShare` | **Profiles** (option): newest story, stories in the last 30 / 90 days, median claps and responses, most used tags, best story of the latest 25, share of member-only stories |
| `editors`, `topAuthors` | **Publication** rows: editors, most frequent authors of the latest 25 stories |
| `postCount`, `relatedTags` | **Tag** rows: stories published under the tag, tags Medium shows as related |
| `emails`, `phones` | **Bio contacts** (option): e-mail addresses and phone numbers written in a bio or description |
| `source`, `rank`, `scrapedAt` | where the row comes from (`tag:python`, `tag:python:2024-03` for a month link, `search:web scraping`, `author:karpathy`, `publication:netflix-techblog`, `followers:karpathy`), its position in that source (1 = first), ISO timestamp of the run |

### 💡 Tips

#### How to get more results

Set `maxItems` to `0`. A tag read with `latest` goes back through its whole archive; with `postedAfter` / `postedBefore` the Actor jumps straight to those months. Medium's rankings (`hot`, `topWeek`…) stop at about 100 articles per tag and a search at about 1,000 per keyword: to go further, add more tags or keywords, or walk a tag month by month with `mostRead`.

#### How to reduce costs

The price is per result, so the levers are `maxItems`, `maxItemsPerQuery`, the filters (a filtered-out article is not charged) and `onlyNew` for recurring runs (you never pay twice for the same article). Each option is charged on its own: tick only the ones you need.

#### Several sources in one run

Tags, keywords, authors, publications, followers lists and links can all be given at once: each is a source of its own. An article found by several sources is saved — and charged — once. Set `maxItemsPerQuery` to give every source its own cap: without it the first sources can use up the whole `maxItems` budget. The same tag given twice, in `tags` and as a link, is one source. A tag with an archive link (`…/tag/python/archive/2024/03`) is a source of its own, next to the whole tag and with its own cap; the rows say which in the `source` column (`tag:python:2024-03`).

#### Monitoring: only the new articles

Tick **New articles only** (`onlyNew`) and schedule the Actor. The first run returns everything; each later run skips the articles already delivered: they are not saved and not charged. The memory lives in a named key-value store of your account (`medium-articles-scraper-seen`, up to 150,000 articles per key) and is only updated with articles that really reached the dataset, so a failed run never hides anything. Give each schedule its own `stateKey` (two schedules sharing a key would hide each other's articles), and tick `resetState` once to start over. A tag read with `latest`, an author and a publication are read newest first and stop once they meet a long run of articles you already have.

#### Filter by publication date

`postedAfter` and `postedBefore` take a date (`2026-09-01`, the whole day is included, UTC, for both: with `postedBefore` the articles of 1 September are kept) or a period before now (`7 days`, `2 weeks`, `1 month`; via the API also `24 hours` or a full ISO date-time). The filter reads `publishedAt`; an article without a publication date is dropped as soon as a date bound is set. Filtered-out articles are not saved and do not count in `maxItems`; the run summary tells how many were filtered. A tag read with `latest` jumps to the months asked, and an author's or a publication's archive stops at the first page that is entirely too old.

### 🔌 Integrations and API

Call the Actor via the Apify API, the JavaScript or Python clients, or connect it with integrations and webhooks (Make, Zapier, n8n, Google Sheets, Slack, Airtable…). The dataset can be fetched as JSON or CSV from any tool.

### 🤖 Use with AI agents (MCP)

AI agents (Claude, ChatGPT, Cursor…) can find and run this Actor through the [Apify MCP server](https://mcp.apify.com), billed to their Apify account like any run. It returns one row per Medium article (or author, publication, tag, response). Actor id: `nice_dev/medium-articles-scraper`; MCP server with this Actor only: `https://mcp.apify.com/?tools=fetch-actor-details,nice_dev/medium-articles-scraper`.

Smallest input, for a cheap first call:

```json
{
    "tags": ["python"],
    "maxItems": 10
}
```

Key output fields: `url`, `title`, `authorName`, `publicationName`, `publishedAt`, `claps`, `responses`, `tags`, and `markdown` (with `includeContent`).

Cost: a price per result plus a small fee per run start, options apart, see the pricing section above. Cap each call with `maxItems` and, through the API, with the run option `maxTotalChargeUsd`.

### ❓ FAQ

#### Is it legal to scrape Medium?

The Actor only reads what Medium shows publicly to any anonymous visitor. It logs in to nothing, and the paywall is never passed: a member-only story gives its public preview only. Results can contain personal data (names, bios, links and contacts that authors publish themselves), which is protected by GDPR: do not store it without a legitimate reason. You are responsible for using the data in compliance with Medium's Terms of Service and applicable law. This Actor is not affiliated with Medium.

#### Does it need a login or a proxy?

No login. The proxy is included in the price: leave the default setting (the residential proxy is not available). A request the site turns away is tried again on a new proxy session, up to 5 times (**Max request retries**), after a pause of 5 seconds, doubled at each retry up to 20 seconds.

#### Can I get the full text of member-only stories?

No. For a member-only story Medium shows anonymous visitors a preview only: with **Include article text**, that preview is what you get, `contentIsPreview` is `true`, and the text option is not charged for it. Open stories come in full.

#### Is the data safe to open in Excel or to show on a web page?

Titles, texts, bios and responses are the authors' own words, copied as they are. A text can begin with `-`, `+`, `=` or `@` (a list, a handle, a title such as `+10 tips`): Excel and Google Sheets may read such a cell of a CSV file as a formula or as a number. The Actor leaves the text as it is, so that the JSON and the API give the real value: when you open a CSV, import these columns as text. The `html` column is built by the Actor from the article's paragraphs; on a web page, escape every field like any text written by a stranger.

#### Known limitations

- Medium's rankings (`hot`, `topWeek`, `topMonth`, `topYear`, `topAllTime`) give about 100 articles per tag, `mostRead` 20 per month, and a search about 1,000 results per keyword.
- `topClappers` holds at most 25 people: Medium shows no more.
- The figures of the profile rows (median claps, stories in the last 30 / 90 days, best story, top tags) are computed on the author's or publication's latest 25 stories.
- `onlyNew` remembers article ids, not their content: an article whose claps changed is not returned again.
- Two runs sharing the same `stateKey` at the same time may both return the same new article.

**A run that reaches its timeout** stops itself about 45 seconds before it: no new page is asked, what it read is saved and, with `onlyNew`, remembered, and the run ends *Succeeded* with "Stopped before the run's timeout". Resurrect it to go on from there, or give the next run a longer timeout (Run options).

**A run the platform stops without warning** (out of memory)

- Resurrect it: it goes on from where it stood at most a minute before the stop. What it had read since is read again, and the rows already saved are skipped: none is delivered or charged twice, and `maxItems` still counts them.
- With `onlyNew`, the memory is saved after each write (at most once every 2 seconds): resurrect the stopped run and the articles it had saved meanwhile join the memory; leave it stopped for good, and the next run may return the articles of its last 2 seconds once more.

#### Something doesn't work?

The last line of the log counts the results saved, filtered out and no longer on Medium (a tag, author or article that does not exist), and the requests that failed after every retry. Those requests and the missing pages are listed, with the reason, in the `FAILED_REQUESTS` record of the run's key-value store. A run that saved nothing and had failed requests fails, and its last message gives the cause.

If Medium changes, you are told instead of paying for blank rows. If the first 20 articles read all lack their title or publication date, the run saves nothing more, stops and fails, and its last message names the missing field: at most those first articles are charged. An article that `postedAfter` / `postedBefore` drops because it has no date at all counts among those 20. A run of fewer articles (a scheduled `onlyNew` run that finds a few new ones, a small **Max results (total)**) is checked at its end, from 5 articles: its articles are already saved, and the run fails with the missing field instead of ending green.

### 🛟 Support

Open an issue in the **Issues** tab with a link to your run: the run log and the `FAILED_REQUESTS` record of the key-value store show exactly which URLs failed and why.

# Actor input Schema

## `startUrls` (type: `array`):

Any Medium link, mixed freely: tag page (`https://medium.com/tag/python`, …/tag/python/archive/2024/03), search (medium.com/search?q=…), author (medium.com/@user or user.medium.com; …/@user/followers and …/@user/following list the people instead of the stories), publication (medium.com/towards-artificial-intelligence or its own domain such as levelup.gitconnected.com), article (any article URL, also on custom domains). Each link is read as its own source. Max 100 URLs.

## `query` (type: `string`):

One keyword searched on Medium, e.g. `web scraping` (stories, or authors / publications / tags with 'Search for'). Medium returns up to about 1,000 results per keyword. Use 'Search keywords' below for several.

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

Several keywords searched one after the other (added to 'Search keyword'). Up to 100 keywords of 200 characters.

## `searchType` (type: `string`):

What the keywords search for: stories (articles), people (author rows), publications (publication rows) or tags (tag rows with their story and follower counts).

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

Medium tag slugs or tag links, e.g. `python`, `data-science`. `C++`, `C#`, `.NET` and `F#` are read as the tags `cpp`, `csharp`, `dotnet` and `fsharp`. Up to 100 tags. Read with the order set in 'Tag feed'.

## `tagSort` (type: `string`):

Which stories of each tag. 'Latest' walks the tag's whole archive, newest first (with 'Published after / before' it jumps straight to those months). 'Most read' = the 20 most-read stories of each month, month by month back from 'Published before' (or today) to 'Published after'. 'Hot', 'Top this week / month / year / all time' = Medium's own rankings, about 100 stories each.

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

Usernames (`karpathy` or `@karpathy`) or profile links (medium.com/@user, user.medium.com). All their stories, newest first. Up to 100 authors.

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

Publication slugs (`netflix-techblog`), own domains (`levelup.gitconnected.com`) or links. Their whole archive, newest first. Up to 100 publications.

## `followersOf` (type: `array`):

Usernames or profile links: one author row per follower (name, username, bio, followers, following, X handle, membership). Newest followers first.

## `followingOf` (type: `array`):

Usernames or profile links: one author row per person they follow.

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

Direct article links (medium.com, writer subdomains, publication domains, or medium.com/p/<id>), e.g. `https://medium.com/p/a64152b37c35`. One row per article. Up to 1 000 links.

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

Maximum results for the whole run: articles, plus the author, publication and tag rows of a search or a followers list (0 = no limit). Responses and the profile rows added by the options are not counted.

## `maxItemsPerQuery` (type: `integer`):

Maximum results from each source: tag, keyword, author, publication, followers list or link (0 = no per-source limit, only the total).

## `maxFollowersPerUser` (type: `integer`):

Rows per user in 'Followers of' / 'Followed by' (0 = all).

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

Adds each article's full text as markdown and plain text (headings, lists, quotes, code, images and links kept), its outbound links, images, headings and paragraph / image / code-block counts. Member-only stories give Medium's public preview only (flagged by 'contentIsPreview'). Billed per article with text.

## `includeHtml` (type: `boolean`):

Also adds the text as simple HTML (needs 'Include article text').

## `compareWithAuthor` (type: `boolean`):

Adds the author's median claps over their latest 25 stories and how this article compares (×). One extra request per author; billed per article compared (not charged when no comparison is possible: median not found, or equal to 0). 'Claps per 1,000 author followers' is always given.

## `includeResponses` (type: `boolean`):

Adds the responses of each article as extra rows (type 'response': text, claps, author, date), up to 'Max responses per article'. Billed per response row.

## `maxResponsesPerArticle` (type: `integer`):

Response rows per article when 'Include responses' is on (0 = all).

## `includeAuthorProfiles` (type: `boolean`):

Adds a profile row (type 'author') for each author of the articles found, and turns the author rows of a search or a followers list into full profiles: followers, following, bio, links, membership, last story date, stories in the last 30 / 90 days, median claps and responses, best story, top tags. One extra request per author; billed per profile row.

## `includePublicationProfiles` (type: `boolean`):

Adds a profile row (type 'publication') for each publication of the articles found, and turns the publication rows of a search into full profiles: followers, description, domain, editors' links, stories in the last 30 / 90 days, median claps and responses, best story, top tags. One extra request per publication; billed per profile row.

## `includeBioContacts` (type: `boolean`):

Author and publication rows: e-mail addresses and phone numbers written in the bio or description ('emails', 'phones'). Billed only for rows where at least one contact is found.

## `postedAfter` (type: `string`):

Keeps articles first published on or after this date (the article's 'publishedAt'): `2026-09-01` (the whole day is included, UTC) or a period before now like `7 days`, `2 weeks`, `1 month`. Tags in 'Latest' / 'Most read' jump straight to these months.

## `postedBefore` (type: `string`):

Keeps articles first published before this date (the article's 'publishedAt'): `2026-09-01` (the whole day is included, UTC: articles of 1 September are kept, those of 2 September are not) or a period before now like `7 days`, `2 weeks`, `1 month`. Tags in 'Latest' / 'Most read' jump straight to these months.

## `excludeKeywords` (type: `array`):

Drops the rows whose title, subtitle or tags contain one of these words (case-insensitive), e.g. `crypto`: articles, and also author, publication and tag rows (judged on their name, and a publication on its tags). Responses are never dropped by it. Up to 50 words of 100 characters.

## `minClaps` (type: `integer`):

Keeps articles with at least this many claps (0 = all). Dropped articles are not billed.

## `minFollowers` (type: `integer`):

Keeps author, publication and tag rows with at least this many followers (0 = all). Dropped rows are not billed.

## `memberOnly` (type: `string`):

All stories, only free stories, or only member-only (paywalled) stories.

## `language` (type: `string`):

Keeps articles in this language only, as detected by Medium. A two-letter code (`en`, `es`, `fr`, `de`, `pt`, `ja`, `zh`…) also keeps its variants (`zh` keeps `zh-hant`); a code with a script (`zh-hant`) keeps exactly that one. Empty = all.

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

Skips articles already returned by a previous run with the same 'State key' — schedule the run to get only new stories of a tag, author, publication or keyword.

## `stateKey` (type: `string`):

Name of the memory of articles already seen, one per monitoring task, e.g. `python-daily` (letters, digits, dashes and underscores, up to 64 characters).

## `resetState` (type: `boolean`):

Forgets the articles already seen under this 'State key' before the run.

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

The Apify proxy is included in the price: keep the default. You can also give your own proxy URLs.

## `maxConcurrency` (type: `integer`):

Requests in parallel.

## `maxRequestsPerMinute` (type: `integer`):

Upper limit of requests per minute for the whole run.

## `minRequestIntervalMs` (type: `integer`):

Pause between two requests, in milliseconds (0 = none).

## `maxRequestRetries` (type: `integer`):

Tries again a request that failed or that the site turned away, each time on a new proxy session (0-10).

## `debugLog` (type: `boolean`):

More detailed log.

## Actor input object example

```json
{
  "startUrls": [],
  "searchQueries": [],
  "searchType": "articles",
  "tags": [
    "artificial-intelligence"
  ],
  "tagSort": "latest",
  "authors": [],
  "publications": [],
  "followersOf": [],
  "followingOf": [],
  "articleUrls": [],
  "maxItems": 100,
  "maxItemsPerQuery": 0,
  "maxFollowersPerUser": 1000,
  "includeContent": false,
  "includeHtml": false,
  "compareWithAuthor": false,
  "includeResponses": false,
  "maxResponsesPerArticle": 25,
  "includeAuthorProfiles": false,
  "includePublicationProfiles": false,
  "includeBioContacts": false,
  "excludeKeywords": [],
  "minClaps": 0,
  "minFollowers": 0,
  "memberOnly": "all",
  "onlyNew": false,
  "stateKey": "default",
  "resetState": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxConcurrency": 8,
  "maxRequestsPerMinute": 600,
  "minRequestIntervalMs": 0,
  "maxRequestRetries": 5,
  "debugLog": false
}
```

# Actor output Schema

## `results` (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 = {
    "tags": [
        "artificial-intelligence"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("nice_dev/medium-articles-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 = {
    "tags": ["artificial-intelligence"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("nice_dev/medium-articles-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 '{
  "tags": [
    "artificial-intelligence"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call nice_dev/medium-articles-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nice_dev/medium-articles-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/alrOzevvsPonfBNKy/builds/BiKWO8UXfky4NXSrj/openapi.json
