# Blog / Dated Content Crawler (`diarmuidr/blog-content-crawler`) Actor

Crawl an entire blog / knowledge base or filter to just the new content.
Supporting relevant AI queries by filtering pages by date

- **URL**: https://apify.com/diarmuidr/blog-content-crawler.md
- **Developed by:** [Diarmuid](https://apify.com/diarmuidr) (community)
- **Categories:** AI, Automation, Developer tools
- **Stats:** 221 total users, 3 monthly users, 100.0% runs succeeded, 12 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## 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

## Blog / Dated Content Crawler

This actor enables you to crawl blog / dated content websites.
What this means is that you can filter the content by its publish date and only keep the content that is newer than a date you select.
This is very useful for AI applications to avoid training your model or feeding your LLM with old / outdated / irrelevant data.

This is also useful for any application where you want to download data from websites such as documentation, help articles, or your knowledge base.

### How it works

- Enter the url(s) (`startUrls`) of the pages / site you want to crawl.
- Optional: Enter a start date (`startDate`) or more likely a "Relative" start date (`relativeStartDate`) to filter the content by. "Relative" means that you can enter a date like "1 month" or "2 years" and the crawler will calculate the date relative to the current date each time it runs.
- Run the crawler
- The crawler will retrieve only the pages that are newer than the start date (`startDate`) you entered or will retrieve all the pages if you don't enter a start date.

### More Details

- **[Input schema](https://apify.com/diarmuidr/blog-content-crawler/input-schema)**
- **[Usage Guide](https://apify.com/diarmuidr/blog-content-crawler/api/javascript)**

# Actor input Schema

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

URLs to start with.

## `relativeStartDate` (type: `string`):

Optional: Select relative start date by which to filter pages. Any post published before this date will be excluded. This is calculated relative to the time the crawler is run (eg: '2 weeks' would include posts from 2 weeks ago to now). Format in format 'X days' or 'X weeks' or 'X months' or 'X years'. If no time period is passed in, it will be treated as a number of seconds.

## `startDate` (type: `string`):

Optional: Select start date by which to filter pages. Any post published before this date will be excluded. Format in format YYYY-MM-DD

## `dateSelector` (type: `string`):

Optional: CSS Selector to identify where the date is located on the page. This can make the date filtering more accurate if we don't get it right on the first round. You should use this if there's multiple dates on the page.

## `includeResultsWithNoDate` (type: `boolean`):

Optional: If true, the crawler will include results where no date was detected on the page.

## `waitForSelector` (type: `string`):

CSS Selector to wait for for page load. Useful when content on the page loads asynchronously. This is optional and in most cases won't be needed.

## `removeElementsCssSelector` (type: `string`):

Optional: CSS Selector to identify elements to remove from the page. This is useful for removing elements that are not needed for the content. For example, you can remove the header and footer of the page.

## `requestTimeoutSecs` (type: `integer`):

Optional: How long the crawler will wait for a request to complete. Default is 60 seconds.

## `dynamicContentWaitSecs` (type: `integer`):

Optional: Used in conjunction with `waitForSelector`. How long the crawler will wait for the selector to appear. Default is 10 seconds.

## `useSitemaps` (type: `boolean`):

Optional: If checked, the crawler will use the sitemap.xml file to find additional URLs to crawl.

## `filterByDocumentLastModified` (type: `boolean`):

Optional: If true, the crawler will include the document's 'last modified' date in the output.

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

Select proxies to be used by your crawler.

## `crawlerType` (type: `string`):

Select the crawler type to use. Adaptive crawlers run faster by automatically switching between raw http and browser requests. Firefox is less likely to be blocked by websites but may ocassionally fail in comparison to chrome. NOTE: You cannot use the adaptive crawler options with the 'filter by document last modified' option.

## `selectUrlsBy` (type: `string`):

Optional: Select URLs by different kinds of patterns. This is similar to includeUrlGlobs but allows for simpler selection. If `includeUrlGlobs` is provided, this option is ignored.
Subpath (default): Selects URLs that are under the path of the startUrls provided.
All: Selects all URLs found.
Same Hostname: Selects URLs that have the same hostname (does not include subdomains).
Same Domain: Selects URLs that have the same domain (i.e includes subdomains).
Same Origin: Selects URLs that have the same hostname and protocol (http/https).

## `includeUrlGlobs` (type: `array`):

Optional: An array of URL globs to include in the crawl. If not provided, the crawler will include only urls under the path of the startUrls provided. You can test globs on https://www.digitalocean.com/community/tools/glob.

## `excludeUrlGlobs` (type: `array`):

Optional: An array of URL globs to exclude from the crawl in order to avoid crawling a particular page or set of pages.

## `maxCrawlPages` (type: `integer`):

Optional: The maximum number of pages to visit. This is useful for limiting the crawl to a specific number of pages to avoid overspending.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.gymshark.com/blog/article/interval-running"
    }
  ],
  "includeResultsWithNoDate": false,
  "removeElementsCssSelector": "nav, footer, script, style, noscript, svg, img[src^='data:'],\n[role=\"alert\"],\n[role=\"banner\"],\n[role=\"dialog\"],\n[role=\"alertdialog\"],\n[role=\"region\"][aria-label*=\"skip\" i],\n[aria-modal=\"true\"]",
  "requestTimeoutSecs": 60,
  "dynamicContentWaitSecs": 10,
  "useSitemaps": true,
  "filterByDocumentLastModified": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "crawlerType": "adaptive:firefox",
  "selectUrlsBy": "subpath"
}
```

# 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 = {
    "startUrls": [
        {
            "url": "https://www.gymshark.com/blog/article/interval-running"
        }
    ],
    "dateSelector": "",
    "waitForSelector": "",
    "removeElementsCssSelector": `nav, footer, script, style, noscript, svg, img[src^='data:'],
[role="alert"],
[role="banner"],
[role="dialog"],
[role="alertdialog"],
[role="region"][aria-label*="skip" i],
[aria-modal="true"]`,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("diarmuidr/blog-content-crawler").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 = {
    "startUrls": [{ "url": "https://www.gymshark.com/blog/article/interval-running" }],
    "dateSelector": "",
    "waitForSelector": "",
    "removeElementsCssSelector": """nav, footer, script, style, noscript, svg, img[src^='data:'],
[role=\"alert\"],
[role=\"banner\"],
[role=\"dialog\"],
[role=\"alertdialog\"],
[role=\"region\"][aria-label*=\"skip\" i],
[aria-modal=\"true\"]""",
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("diarmuidr/blog-content-crawler").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 '{
  "startUrls": [
    {
      "url": "https://www.gymshark.com/blog/article/interval-running"
    }
  ],
  "dateSelector": "",
  "waitForSelector": "",
  "removeElementsCssSelector": "nav, footer, script, style, noscript, svg, img[src^='\''data:'\''],\\n[role=\\"alert\\"],\\n[role=\\"banner\\"],\\n[role=\\"dialog\\"],\\n[role=\\"alertdialog\\"],\\n[role=\\"region\\"][aria-label*=\\"skip\\" i],\\n[aria-modal=\\"true\\"]",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call diarmuidr/blog-content-crawler --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,diarmuidr/blog-content-crawler"
        }
    }
}
```

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/k6hpo6ZVOwvh5zb1O/builds/Rv6dlK7tvBkdjh7e1/openapi.json
