# Four Corners Events Scraper (`axiomworks/four-corners-events-scraper`) Actor

Collect events from Four Corners' What's on pages and receive structured records with titles, event types, dates, times, images, ticket links and descriptions. Use a type-filtered listing or a single event page, and optionally add past event pages listed in the sitemap.

- **URL**: https://apify.com/axiomworks/four-corners-events-scraper.md
- **Developed by:** [Axiom Works](https://apify.com/axiomworks) (community)
- **Categories:** News
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.79 / 1,000 results

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

**Returns:** one row per Four Corners event with `title`, `eventTypes`, `dateText`, `startDate`, `endDate`, `startTime`, `ticketUrl` and `description`.
**Input:** `{"startUrls": [{"url": "https://www.fourcornersfilm.co.uk/whats-on"}], "includePastEvents": true, "maxItems": 5}`
**Best for:** London arts event calendars and listings sites, tracking new exhibitions and screenings, and research on the venue's programme since 2016.

```json
{
    "title": "In Solidarity: Participants Film Screening",
    "eventTypes": ["Talk", "Film screening"],
    "dateText": "Thu 15 October 2026 | 18:30",
    "startDate": "2026-10-15",
    "startTime": "18:30",
    "ticketUrl": "https://www.outsavvy.com/event/39897/in-solidarity-participants-film-screening",
    "sourceUrl": "https://www.fourcornersfilm.co.uk/whats-on/in-solidarity-participants-film-screening-1"
}
```

### What does Four Corners Events Scraper do?

Four Corners Events Scraper is a Four Corners scraper that collects the events published on the What's on pages of fourcornersfilm.co.uk, the website of Four Corners, a film centre in Bethnal Green. It reads each event's own page and gives you one clean row per event: the title, the event type labels (exhibition, talk, film screening, training, project, open call and so on), the date line as the site shows it, ISO start and end dates, the start time when there is one, the main image, the booking link and the full event text.

You can point it at the current What's on listing, at the listing filtered to one event type (for example only film screenings), or at single event pages. Switch on past events and it also walks the site's sitemap, which lists every event page back to 2016, so you can build a full archive of the programme in one run. There is no official Four Corners API for this data; this Actor works as a simple Four Corners API: send a start URL, get structured JSON back.

It reads only public pages, needs no login and no account on the site, and is gentle: about one request per second.

### What data can you get from Four Corners?

Every record comes from the event's detail page. Fields that a page doesn't have are `null`, never invented.

| Field | Description | Example |
|---|---|---|
| `id` | Stable ID of the event: its URL slug, unique within a run | `heartfield-one-man-s-war` |
| `title` | Event title as shown on the event page | `Heartfield: One Man’s War` |
| `eventTypes` | Event type labels from the page | `["Talk", "Film screening"]` |
| `dateText` | Date line as the site shows it, with the time after `\|` when given | `Thu 15 October 2026 \| 18:30` |
| `startDate` | First date in `dateText`, as YYYY-MM-DD | `2026-10-15` |
| `endDate` | Date after the dash in `dateText`, as YYYY-MM-DD; the same as `startDate` for one-day events | `2026-10-15` |
| `startTime` | Start time (HH:MM, London time) when the date line gives one | `18:30` |
| `imageUrl` | Absolute URL of the event's main image | `https://www.fourcornersfilm.co.uk/uploads/...png` |
| `ticketUrl` | First booking, ticket or registration link in the event text | `https://www.outsavvy.com/event/39897/...` |
| `description` | Full event text, one paragraph or heading per line | `This exhibition revisited the father of photomontage...` |
| `slug` | URL slug of the event page, the part after `/whats-on/` | `heartfield-one-man-s-war` |
| `sourceUrl` | Event page the record was read from | `https://www.fourcornersfilm.co.uk/whats-on/heartfield-one-man-s-war` |
| `scrapedAt` | When the record was scraped (ISO 8601, UTC) | `2026-10-12T19:55:11.765Z` |

Archive coverage varies: older pages may lack images, booking links or times, and a few descriptions contain only a single character. Missing fields remain null; descriptions are returned as published.

### How to use Four Corners Events Scraper

1. Open Four Corners Events Scraper in Apify Console and go to the Input tab.
2. In **Start URLs**, keep the prefilled What's on listing, or paste the listing with a type filter, or paste one or more event page URLs.
3. Turn on **Include past events** if you want the archive too, and set **Maximum results**.
4. Click **Start**. A run with the default settings finishes in well under a minute; a full archive of around 160 events takes about three minutes.
5. Open the Output tab to see the events as a table, or export them as JSON, CSV, Excel, XML or RSS.

Tips:

1. `maxItems` is one budget shared by all start URLs. Current listing events are saved first, then past events from the sitemap, so a small `maxItems` gives you what is on now.
2. The same event is saved only once, even when two start URLs (for example the full listing and a filtered one) both list it.
3. To collect only one kind of event, use the listing's type filter, for example `https://www.fourcornersfilm.co.uk/whats-on?type=film-screening`. With past events switched on, the archive is filtered to the same type.
4. Schedule the Actor daily or weekly on the Schedules tab to catch new events as soon as they are published.

### Input

Optional filters combine with your targets and listing type filter. `dateFrom` and `dateTo` use inclusive YYYY-MM-DD bounds: an exhibition spanning the window qualifies even if it began earlier. Either bound can stand alone. When a date filter is active, events without a parsed start date are excluded. `keyword` matches a literal phrase in title or description, ignoring case and normalizing Unicode. Empty values preserve the existing behavior.

For repeat collection, paste previously saved `id` values into `excludeIds`. Known slugs are skipped before fetching their detail pages; they consume neither result charges nor the shared `maxItems` budget. Remove an ID to fetch updates to that event. Filters apply to direct event URLs as well as listings and archive events. Archive filtering still requires `includePastEvents: true` and a valid listing. A selective filter may inspect many pages before finding a match; a run with no results stops searching after approximately 70 seconds and succeeds with guidance to loosen the filters.

| Field | Type | Required | Description |
|---|---|---|---|
| `startUrls` | array of URLs | Yes | What's on listing (`https://www.fourcornersfilm.co.uk/whats-on`), the listing with `?type=<slug>`, or event pages (`https://www.fourcornersfilm.co.uk/whats-on/<event-slug>`). Strings or `{"url": ...}` objects; bare event slugs such as `heartfield-one-man-s-war` also work. |
| `includePastEvents` | boolean | No | Also return past events from the sitemap, after the current ones. Needs a listing start URL that loads. Default `false`; directly supplied historical event pages still work. Prefill `true` plus `maxItems: 5` returns a small sample. |
| `dateFrom` | string | No | Inclusive lower date bound on event overlap, YYYY-MM-DD. |
| `dateTo` | string | No | Inclusive upper date bound on event overlap, YYYY-MM-DD. |
| `keyword` | string | No | Literal phrase matched in title or description, ignoring case. |
| `excludeIds` | string array | No | Previously saved event IDs to skip before fetching. |
| `maxItems` | integer | No | Maximum number of events across all start URLs, 1-10000, default 50. |
| `proxyConfiguration` | object | No | Proxy settings. Default: no proxy (the site answers direct requests). Blocked requests are retried through the proxy you set on a new IP. |

The type filter accepts the slugs the site uses: `exhibition`, `guest-exhibition`, `talk`, `training`, `project`, `artist-support`, `film-screening` and `open-call`.

What happens with unusual input: a URL on another website, or a Four Corners page that isn't a What's on page, is skipped with an error in the log. An event page that no longer exists (HTTP 404) returns no row for that URL. If nothing usable is left, the run still ends successfully with 0 results and a status message that says which URL to fix. Past events are only added when a real What's on listing loaded, so a dead start URL never fills your dataset with the archive.

```json
{
    "startUrls": [
        { "url": "https://www.fourcornersfilm.co.uk/whats-on" },
        { "url": "https://www.fourcornersfilm.co.uk/whats-on/heartfield-one-man-s-war" }
    ],
    "includePastEvents": true,
    "maxItems": 50,
    "proxyConfiguration": { "useApifyProxy": false }
}
```

### Output

Each event is one item in the run's dataset. These two items come from a real run, one archive exhibition and one current film screening:

```json
[
    {
        "id": "heartfield-one-man-s-war",
        "slug": "heartfield-one-man-s-war",
        "title": "Heartfield: One Man’s War",
        "eventTypes": ["Exhibition"],
        "dateText": "Thu 31 October 2019 – Sat 8 February 2020",
        "startDate": "2019-10-31",
        "endDate": "2020-02-08",
        "startTime": null,
        "imageUrl": null,
        "ticketUrl": null,
        "description": "This exhibition revisited the father of photomontage, John Heartfield. Armed with scissors, paste and acerbic wit, Heartfield used art as a political weapon. Risking his life under Hitler’s Third Reich, he subverted Nazi imagery to reveal the political threats of 1930s Germany.\n33 of Heartfield’s scathingly satirical artworks were shown alongside a fascinating collection of historic artefacts.\nThis exhibition was curated by Four Corners in collaboration with John Hyatt.\nSupported using public funding by the National Lottery through Arts Council England, the Barry Amiel & Norman Melburn Trust and the Paul Mellon Centre. Part of Insiders/Outsiders Festival, which celebrates the contribution of refugees from Nazi Europe to British culture.",
        "sourceUrl": "https://www.fourcornersfilm.co.uk/whats-on/heartfield-one-man-s-war",
        "scrapedAt": "2026-10-12T19:55:11.765Z"
    },
    {
        "id": "in-solidarity-participants-film-screening-1",
        "slug": "in-solidarity-participants-film-screening-1",
        "title": "In Solidarity: Participants Film Screening",
        "eventTypes": ["Talk", "Film screening"],
        "dateText": "Thu 15 October 2026 | 18:30",
        "startDate": "2026-10-15",
        "endDate": "2026-10-15",
        "startTime": "18:30",
        "imageUrl": "https://www.fourcornersfilm.co.uk/uploads/1790010977.6195/In%20solidairty_Detail%20Page.png",
        "ticketUrl": "https://www.outsavvy.com/event/39897/in-solidarity-participants-film-screening",
        "description": "Image credit: The Battle of Brick Lane by Nileema Yesmin\nIn Solidarity: Participants Film Screening\nThursday 15th October 2026 at 6:30 PM\nBook tickets here\nThis evening screening event showcases the work of six local people who created short films inspired by the Battle of Cable Street as part of the East End Stories \"In Solidarity\" project.\nFilms by Shirin Begum, Fela Callahan, Hester Gallen, George Ritchie, and Nileema Yesmin.\nEast End Stories explores radical histories in East London, using photography and film. The project is supported through the London Borough of Tower Hamlets Mayor's Community Grants Programme.\nAs part of our Solidarity on the Streets exhibition (2 Oct - 31 Oct 2026) Four Corners is hosting events to discuss the legacy of the Battle of Cable Street and activism against racism, fascism and exclusion.",
        "sourceUrl": "https://www.fourcornersfilm.co.uk/whats-on/in-solidarity-participants-film-screening-1",
        "scrapedAt": "2026-10-12T19:55:12.653Z"
    }
]
```

You can download the dataset as JSON, CSV, Excel, XML or RSS from the Output tab, or read it through the Apify API.

### How much does it cost to scrape Four Corners?

Four Corners Events Scraper is charged per result: you pay for each event saved to the dataset and nothing for URLs that return no event. `maxItems` caps how many results a run can return, and so caps the charge. See the Pricing tab for the current price. Because the site is small, even a full archive run returns only around 160 events.

### Use with AI agents (MCP)

Connect Four Corners Events Scraper to Claude, Cursor or any MCP client through the Apify MCP server:

```json
{
    "mcpServers": {
        "apify": {
            "url": "https://mcp.apify.com/?tools=axiomworks/four-corners-events-scraper"
        }
    }
}
```

Example prompts:

1. "What's on at Four Corners in Bethnal Green this month? List the title, dates and booking link for each event."
2. "Find every film screening Four Corners has held since 2016 and give me a table of titles and dates."
3. "Get the Four Corners exhibitions that are running now and summarise each description in one sentence."

For a quick sample, send `{"startUrls":[{"url":"https://www.fourcornersfilm.co.uk/whats-on"}],"includePastEvents":true,"maxItems":5}`. This is the prefill tested locally with five live records. For one event, `{"startUrls":["heartfield-one-man-s-war"],"maxItems":1}` accepts its slug directly. The schema prefill is a Console example; API and MCP callers must supply `startUrls` themselves. Omit `includePastEvents` to use its default `false`; that restricts listing discovery to current events but still accepts a historical event URL.

The agent sends a start URL and gets the same JSON rows as the dataset, so it can filter by `eventTypes`, sort by `startDate`, or link to `ticketUrl` directly.

### Integrations and API

Run Four Corners Events Scraper from your own code with the Apify API. Set `APIFY_TOKEN` to your Apify API token (send it in the `Authorization` header, never in the URL).

**Python**

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("axiomworks/four-corners-events-scraper").call(run_input={
    "startUrls": [{"url": "https://www.fourcornersfilm.co.uk/whats-on"}],
    "includePastEvents": True,
    "maxItems": 50,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["startDate"], item["title"])
```

**JavaScript**

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

const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });
const run = await client.actor('axiomworks/four-corners-events-scraper').call({
    startUrls: [{ url: 'https://www.fourcornersfilm.co.uk/whats-on' }],
    includePastEvents: true,
    maxItems: 50,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

**cURL**

```bash
curl -X POST "https://api.apify.com/v2/acts/axiomworks~four-corners-events-scraper/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"startUrls": [{"url": "https://www.fourcornersfilm.co.uk/whats-on"}], "maxItems": 50}'
```

The Integrations tab connects the Actor to Make, Zapier, n8n, Google Sheets, Slack and webhooks, so new events can flow straight into a spreadsheet or a calendar tool after each scheduled run.

### FAQ

**How many events does the What's on listing have?**
The current listing is small, usually a handful of exhibitions, screenings, talks and courses (six when this Actor was built). Turn on past events to add the archive of roughly 160 event pages back to 2016.

**Why did my run return 0 results?**
The status message says why. Usually the start URL points at an event page that was removed, at a page outside What's on, or at a listing type with nothing scheduled right now. Check the URL in a browser. For an empty filtered listing, remove or change `?type=`; alternatively switch on past events with a listing URL to get archive events of that type. A dead event URL does not trigger an archive fallback.

**Why do some events have no start time or end date of their own?**
Exhibitions and courses run over a date range without a time, so `startTime` is `null`. One-day events have no second date, so `endDate` is the same as `startDate`. The original date line is always in `dateText`.

**Why is `ticketUrl` empty for many events?**
Only events that link to a booking page (for example OutSavvy or Eventbrite) have one. Free exhibitions and most archive pages have no booking link.

**How fresh is the data?**
Every run reads the live website, so the data is as current as the site. Schedule runs to keep a calendar up to date.

**Do I need a proxy?**
No. The site answers direct requests. Challenge detection checks the page title and visible text, so ordinary pages that load an anti-bot script are not treated as blocked. If a request is ever blocked, it is retried with backoff; set a proxy in the input if blocks keep happening.

**Can I scrape a single event?**
Yes. Put its page URL (`https://www.fourcornersfilm.co.uk/whats-on/<event-slug>`) in Start URLs. Each event page returns one row.

### Is it legal to scrape Four Corners?

This Actor collects only publicly available event information that anyone can read on the Four Corners website without logging in. It doesn't collect personal data beyond names that appear in public event descriptions. You are responsible for how you use the data, including any rules on personal data such as the GDPR, and for respecting the site's terms. This Actor is an independent tool and is not affiliated with, endorsed by or connected to Four Corners. If you are unsure about your use case, ask a lawyer.

### Feedback

Found a bug or need a field that isn't here? Open an issue on the Issues tab and we'll look at it.

# Actor input Schema

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

Four Corners targets as URL strings or {"url":"..."} objects, e.g. \[{"url":"https://www.fourcornersfilm.co.uk/whats-on"}] for current events, \["https://www.fourcornersfilm.co.uk/whats-on?type=film-screening"] for screenings, or \["heartfield-one-man-s-war"] for one event. Bare host and trailing slash forms work. maxItems is shared across targets; duplicates appear once. Other hosts/paths, dead or blocked targets are skipped; if none work, the run succeeds with 0 rows.

## `includePastEvents` (type: `boolean`):

Set true, e.g. {"includePastEvents":true}, to add sitemap archive events after listing events. Default false returns only events linked by your targets, including historical event URLs supplied directly. Requires a real listing URL that loads; event-only or dead targets never trigger the archive. A listing ?type=film-screening also filters archive records to that type. Prefill true with maxItems 5 gives a small sample, not the full archive.

## `dateFrom` (type: `string`):

Keep events whose endDate is on or after this inclusive YYYY-MM-DD date. Default empty adds no lower bound. Uses London calendar dates; events with no parsed startDate are excluded when a date bound is set. Works on direct events, listings and archives.

## `dateTo` (type: `string`):

Keep events whose startDate is on or before this inclusive YYYY-MM-DD date. Default empty adds no upper bound. Combine with dateFrom for overlapping events, including exhibitions starting before the window. Must be on or after dateFrom.

## `keyword` (type: `string`):

Keep events containing this literal phrase in title or description, ignoring case and normalizing Unicode. Default empty keeps all. Combined with date and listing type filters. Requires fetching event detail pages; a phrase with no matches returns 0 rows successfully.

## `excludeIds` (type: `array`):

Skip event IDs (URL slugs) already collected, e.g. \["heartfield-one-man-s-war"]. Default \[] skips none. Known IDs are skipped before their detail request and do not consume maxItems or result charges. New matching events retain source order.

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

Limit the total saved events across all targets, e.g. {"maxItems":5}. Integer 1-10000, default 50; prefill 5. Stops at this limit or your run spending limit, whichever comes first. Returns fewer when targets contain fewer matching events. Listing and directly supplied events precede sitemap archive events; duplicate events count once.

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

Choose request routing, e.g. {"proxyConfiguration":{"useApifyProxy":false}} for direct requests (default). Use the proxy editor to enable Apify Proxy when the site blocks requests. Retryable HTTP errors and block pages use backoff and a fresh proxy session when configured. Targets still blocked after retries are skipped; an entirely blocked run succeeds with 0 rows and asks you to retry or change proxy settings.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.fourcornersfilm.co.uk/whats-on"
    }
  ],
  "includePastEvents": true,
  "dateFrom": "",
  "dateTo": "",
  "keyword": "",
  "excludeIds": [],
  "maxItems": 5,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

Dataset with one item per result, in the order found.

# 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.fourcornersfilm.co.uk/whats-on"
        }
    ],
    "includePastEvents": true,
    "maxItems": 5,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("axiomworks/four-corners-events-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 = {
    "startUrls": [{ "url": "https://www.fourcornersfilm.co.uk/whats-on" }],
    "includePastEvents": True,
    "maxItems": 5,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("axiomworks/four-corners-events-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 '{
  "startUrls": [
    {
      "url": "https://www.fourcornersfilm.co.uk/whats-on"
    }
  ],
  "includePastEvents": true,
  "maxItems": 5,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call axiomworks/four-corners-events-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,axiomworks/four-corners-events-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/90GthPpX1pzfegb6b/builds/X2DBuMK4PKIpXXGoL/openapi.json
