# News and Blog Article Extractor: Clean Text, Author, Date (`fernandosord/article-extractor`) Actor

Paste news or blog URLs, or a section page, RSS feed or sitemap to discover them. Get one row per article: clean text, Markdown or HTML, author, publish and update dates, title, site name, language, tags, lead image and word count. Reads JSON-LD and OpenGraph. No browser, no login.

- **URL**: https://apify.com/fernandosord/article-extractor.md
- **Developed by:** [Plainsource](https://apify.com/fernandosord) (community)
- **Categories:** News, AI
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 articles

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

This Actor turns news and blog URLs into clean article data. For every link you give it, you get the article text (plain text, Markdown or HTML), the headline, author names, publish and update dates, site name, language, tags, lead image and word count. You can also give it a section page, an RSS feed or a sitemap and it finds the article links itself. It reads the page's JSON-LD and OpenGraph data first and uses Mozilla Readability for the body text. No browser, no login, no API key.

- **Metadata from structured data first.** Dates, authors, tags and the article type come from JSON-LD (NewsArticle, BlogPosting, Article), OpenGraph and meta tags, with the visible page as a fallback.
- **Three body formats.** Plain text with paragraph breaks, Markdown with absolute links, or cleaned HTML. Pick any combination.
- **Pages that are not articles are dropped.** Home pages, section pages, tag pages and link lists are skipped and not charged. The reason for each is written to the `SUMMARY` record.
- **Find articles on a section.** Give a blog home, a news section, an RSS or Atom feed, or a sitemap, and set how many articles you want.
- **Paywall flag.** When the page markup says the article is not free, `isPaywalled` is true and you get whatever text is public in the HTML.

### What data you get

| Field | What it holds |
|---|---|
| `url`, `inputUrl`, `canonicalUrl` | Final address after redirects, the link you gave, and the canonical address the site declares |
| `title` | Headline, without the site name suffix |
| `authors`, `author` | Author names as a list and joined with commas |
| `publishedAt`, `modifiedAt` | ISO 8601 dates when the page declares them |
| `siteName`, `language` | Publication name and language code |
| `description`, `section`, `tags` | Summary, section or category, keywords |
| `articleType` | The JSON-LD type, for example `NewsArticle` or `BlogPosting` (null when the page has none) |
| `isPaywalled` | True only when the markup says the article is not free |
| `wordCount`, `readingTimeMinutes` | Length of the extracted text and reading time at 230 words per minute |
| `leadImage`, `images` | Main image, and the images inside the article with alt text and captions |
| `text`, `markdown`, `html` | The article body in the formats you selected |
| `discoveredFrom` | The section, feed or sitemap the article was found on (null for URLs you gave) |

### How to use it

1. Paste article URLs into **Article URLs**.
2. Optional: add a section page, RSS feed or sitemap to **Find articles on these pages**, and set **Max articles per section**.
3. Choose the body formats you want and whether to include images.
4. Run it and export to JSON, CSV or Excel, or call it from the API, Make, Zapier, n8n, LangChain or an AI agent through the Apify MCP server.

A feed or sitemap URL also works inside **Article URLs**; the Actor notices it is not a page and follows its links. Discovery looks at the first page of a section (plus up to three further pages when the site marks a next page). For a deep archive, use the sitemap URL instead.

#### Example input

```json
{
  "urls": [
    "https://www.theguardian.com/world/2026/nov/01/ukraine-defies-trump-resumes-attacks-russian-oil"
  ],
  "discoveryUrls": ["https://blog.cloudflare.com/rss/"],
  "maxArticlesPerSection": 2,
  "outputFormats": ["text", "markdown"],
  "includeImages": true
}
```

#### Example output (one article, text and Markdown shortened)

```json
{
  "url": "https://www.theguardian.com/world/2026/nov/01/ukraine-defies-trump-resumes-attacks-russian-oil",
  "inputUrl": "https://www.theguardian.com/world/2026/nov/01/ukraine-defies-trump-resumes-attacks-russian-oil",
  "canonicalUrl": "https://www.theguardian.com/world/2026/nov/01/ukraine-defies-trump-resumes-attacks-russian-oil",
  "title": "Ukraine defies Trump to resume attacks on Russian oil refineries",
  "authors": ["Luke Harding"],
  "author": "Luke Harding",
  "publishedAt": "2026-11-01T17:20:18.000Z",
  "modifiedAt": "2026-11-02T05:30:55.000Z",
  "siteName": "the Guardian",
  "language": "en",
  "description": "Kremlin says Kyiv is simply ignoring US wishes days before midterm elections after reported strikes on two oil sites",
  "section": "World news",
  "tags": ["Ukraine", "Russia", "Donald Trump", "Trump administration"],
  "articleType": "NewsArticle",
  "isPaywalled": false,
  "wordCount": 789,
  "readingTimeMinutes": 3,
  "leadImage": "https://i.guim.co.uk/img/media/e521bd30aafd13837c587fdc1c0f6c632ad8d0fd/1585_152_2231_1784/master/2231.jpg",
  "images": [
    {
      "url": "https://i.guim.co.uk/img/media/a68d2e8389b9569e2e5cd62f5e1673e2636d6a1f/2318_0_7612_6090/master/7612.jpg",
      "alt": "A satellite image shows smoke rising from the Novokuybyshevsk oil refinery",
      "caption": "A satellite image shows smoke rising from the Novokuybyshevsk oil refinery in the Samara region of Russia after a reported Ukrainian drone attack on Friday."
    }
  ],
  "text": "Ukraine has resumed its attacks on Russian oil refineries in defiance of pressure from Donald Trump ahead of the US midterm elections, as a ceasefire in the occupied town of Oleshky ended on Sunday night.\n\nRussian state media said on Sunday Ukrainian long-rang...",
  "markdown": "Ukraine has resumed its attacks on Russian oil refineries in defiance of pressure from [Donald Trump](https://www.theguardian.com/us-news/donaldtrump) ahead of the US midterm elections...",
  "discoveredFrom": null,
  "scrapedAt": "2026-11-02T07:55:03.703Z"
}
```

Image URLs and text are shortened here. The real output holds the full values.

### How much does it cost?

You pay **$0.002 per article** saved to the dataset, plus a one-time start fee of $0.0005 per run. URLs that fail to load or turn out not to be articles save nothing and cost nothing.

| Articles | Cost |
|---|---|
| 100 | $0.20 |
| 1,000 | $2.00 |
| 10,000 | $20.00 |

(The start fee of $0.0005 is added once per run.)

### Use cases

- **AI and RAG pipelines.** Feed clean article Markdown into a vector store or an LLM without navigation, ads and cookie banners.
- **Media monitoring.** Take the links from an alert or a feed and get the full text, author and date for each one.
- **Research and archiving.** Save articles with their publish date, update date and canonical URL.
- **Content analysis.** Compare word counts, tags and publish times across publications.
- **Newsletter and digest building.** Pull the headline, summary, lead image and reading time for a list of stories.

### FAQ

**Does it work on any site?** It works on pages whose article text is in the HTML the server sends. Pages that build their content with JavaScript after loading (some single-page apps) return little or nothing and are skipped as non-articles. For those, use an RSS feed or sitemap to discover them, or a browser-based scraper.

**Why did a URL I gave return no row?** The Actor decided it was not an article: a home page, a section page, a page with very little text, or a page that is mostly links. The reason is in the `SUMMARY` record in the run's key-value store. Pages with no article markup need at least **Minimum words** (120 by default) of readable text and a date, an author or several running paragraphs. A real article that is mostly a list of links can be skipped by mistake.

**What about paywalls?** The Actor does not log in and does not bypass paywalls. It returns the text that is in the public HTML, usually the opening paragraphs. `isPaywalled` is true when the page's markup (`isAccessibleForFree` false) says so; a paywall that is not declared in the markup is not detected.

**Some sites return HTTP 403.** Large publishers often block data center addresses. Turn on Apify Proxy in the input (residential proxy works best). In my tests from a home connection, nytimes.com, ft.com, economist.com and theatlantic.com answered 403; I did not test them through a proxy.

**Are authors always filled in?** Only when the site publishes them in its markup or the visible byline can be read. Some blogs publish no author data, and then `authors` is empty.

**How are dates formatted?** As ISO 8601 in UTC. A date without a time stays date-only. Dates the page does not declare are null.

**How does discovery pick links?** It reads the page's links on the same site, drops navigation, tag, author and login links, and ranks the rest by how much they look like articles (date in the path, a long slug, a headline as link text). It checks them in that order until it has the number of articles you asked for. For a section it also reads the site's RSS feed when the page links to one.

**Does it respect robots.txt?** No. It fetches the pages you point it at, like a browser would. You are responsible for using the content in line with the site's terms and copyright.

### Other Actors by this developer

- [Google News RSS Scraper](https://apify.com/fernandosord/google-news-rss-scraper): find news stories by keyword, then pass the links here for the full text.
- [Sitemap URL Extractor](https://apify.com/fernandosord/sitemap-url-extractor): list every URL in a site's sitemap.
- [Document to Markdown](https://apify.com/fernandosord/document-to-markdown): convert PDFs, Word and other files to Markdown.
- [Hacker News Scraper](https://apify.com/fernandosord/hacker-news-scraper): collect stories and links, then extract the articles they point to.
- [Website Contact Extractor](https://apify.com/fernandosord/website-contact-extractor): emails, phone numbers and social links from a site.

# Actor input Schema

## `urls` (type: `array`):

Links to news articles or blog posts. Each one becomes one row. A link to an RSS feed or sitemap is also accepted here and its articles are followed.

## `discoveryUrls` (type: `array`):

A section page, blog home, RSS or Atom feed, or sitemap. The Actor collects article links on the same site, newest first, and extracts them. Pages that turn out not to be articles are skipped and not charged.

## `maxArticlesPerSection` (type: `integer`):

How many articles to extract from each page in the field above.

## `maxArticles` (type: `integer`):

Hard limit on rows for the whole run, direct URLs and discovered articles together. The run stops when it is reached.

## `outputFormats` (type: `array`):

Which versions of the article body to include in each row. Plain text has paragraphs separated by blank lines. HTML is the cleaned article markup with absolute links.

## `includeImages` (type: `boolean`):

Adds an images list (URL, alt text, caption) with the pictures inside the article body, and keeps image links in the Markdown and HTML. The lead image is always included.

## `minWords` (type: `integer`):

Pages that declare themselves as articles (JSON-LD Article, NewsArticle or BlogPosting) are accepted from 25 words. Pages with no such markup need at least this many words of readable text, otherwise they are treated as non-articles.

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

Optional. Most news and blog sites load without a proxy. If a site answers HTTP 403, turn on Apify Proxy (residential works best for hard sites).

## Actor input object example

```json
{
  "urls": [
    "https://paulgraham.com/greatwork.html",
    "https://www.theguardian.com/world/2026/nov/01/ukraine-defies-trump-resumes-attacks-russian-oil"
  ],
  "maxArticlesPerSection": 10,
  "maxArticles": 20,
  "outputFormats": [
    "text",
    "markdown"
  ],
  "includeImages": true,
  "minWords": 120,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

One item per article that was extracted. Pages that are not articles or failed to load are not saved and not charged; they are listed in the SUMMARY record.

# 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 = {
    "urls": [
        "https://paulgraham.com/greatwork.html",
        "https://www.theguardian.com/world/2026/nov/01/ukraine-defies-trump-resumes-attacks-russian-oil"
    ],
    "maxArticles": 20,
    "outputFormats": [
        "text",
        "markdown"
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("fernandosord/article-extractor").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 = {
    "urls": [
        "https://paulgraham.com/greatwork.html",
        "https://www.theguardian.com/world/2026/nov/01/ukraine-defies-trump-resumes-attacks-russian-oil",
    ],
    "maxArticles": 20,
    "outputFormats": [
        "text",
        "markdown",
    ],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("fernandosord/article-extractor").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 '{
  "urls": [
    "https://paulgraham.com/greatwork.html",
    "https://www.theguardian.com/world/2026/nov/01/ukraine-defies-trump-resumes-attacks-russian-oil"
  ],
  "maxArticles": 20,
  "outputFormats": [
    "text",
    "markdown"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call fernandosord/article-extractor --silent --output-dataset

```

## MCP server setup

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

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/petP1Ko4mizJAOhVC/builds/rUnOUvSAUB9dAUc9e/openapi.json
