# OCaml Packages Scraper (`axiomworks/ocaml-packages-scraper`) Actor

Scrape public OCaml.org package pages and return structured records for package versions, including synopses, descriptions, licenses, authors, maintainers, dependencies, checksums and publish dates. Search by free text or author filter, or provide package names and version URLs.

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

## Pricing

from $7.00 / 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 OCaml package version with `name`, `version`, `synopsis`, `license`, `authors`, `dependencies`, `checksums`, `publishedAt`.
**Input:** `{"searchQueries": ["yaml"], "maxItems": 50}`
**Best for:** dependency audits of OCaml projects, license and supply-chain checks, tracking new releases of opam packages.

```json
{
    "name": "lwt",
    "version": "6.1.2",
    "isLatest": true,
    "synopsis": "Promises and event-driven I/O",
    "license": "MIT",
    "publishedAt": "2026-04-29",
    "installCommand": "opam install lwt",
    "usedByCount": 511
}
```

### What does OCaml Packages Scraper do?

OCaml Packages Scraper turns the package pages of ocaml.org into clean JSON. ocaml.org has no public per-package JSON API, so this Actor works as an OCaml package API: give it search queries (the same ones you would type into the ocaml.org package search, including filters such as `author:"Jérôme Vouillon"`) or a list of opam package names and URLs, and it returns one structured record per package version.

Each record holds the package name and version, whether that version is the latest, the one-line synopsis and the longer description, the license, homepage, opam file link and number of published versions, the authors and maintainers, the source archive URL with its checksums, the date the version was added to opam-repository, the install command, and the full dependency lists (runtime and development) with their version constraints. It also counts how many packages depend on this one.

The Actor reads public pages only. No login, no account and no API key for ocaml.org are needed. It is useful for dependency audits, license reviews, software bills of materials, release monitoring, and building datasets of the OCaml ecosystem.

### What data can you get from ocaml.org?

| Field | Description | Example |
|---|---|---|
| `id` | Stable record ID: `name@version` | `lwt@6.1.2` |
| `name` | opam package name | `lwt` |
| `version` | Package version | `6.1.2` |
| `isLatest` | True when this is the newest version | `true` |
| `synopsis` | One-line summary of the package | `Promises and event-driven I/O` |
| `description` | Longer description, paragraphs separated by a blank line; null when there is none | `A promise is a value that may become determined in the future.` |
| `license` | License from the license link, without the word "License", or the first sentence of the README License paragraph; null when both are absent | `MIT` |
| `homepage` | Project homepage or repository | `https://github.com/ocsigen/lwt` |
| `opamFileUrl` | The opam file in opam-repository | `https://github.com/ocaml/opam-repository/blob/master/packages/lwt/lwt.6.1.2/opam` |
| `versionsCount` | Number of published versions | `32` |
| `authors` | Author names | `["Jérôme Vouillon", "Jérémie Dimino"]` |
| `maintainers` | Maintainer names, sometimes with an email | `["Raphaël Proust <code@bnwr.net>", "Anton Bachin"]` |
| `sourceArchiveUrl` | Download URL of the release source archive | `https://github.com/ocsigen/lwt/archive/refs/tags/6.1.2.tar.gz` |
| `checksums` | Archive checksums as `algorithm=hex` | `["md5=00bc7eb2772a28530f7365a7040f0c4f", "sha512=a473..."]` |
| `publishedAt` | Date added to opam-repository (YYYY-MM-DD) | `2026-04-29` |
| `installCommand` | opam command that installs this version | `opam install lwt` |
| `dependencies` | Dependencies as `{name, constraint}` objects | `[{"name": "dune", "constraint": ">= \"3.18\""}]` |
| `devDependencies` | Test, doc and dev dependencies as `{name, constraint}`; empty when none | `[{"name": "odoc", "constraint": "with-doc & >= \"2.3\""}]` |
| `dependenciesCount` | Number of dependencies | `5` |
| `usedByCount` | Number of packages that depend on this one; 0 when none | `511` |
| `docsUrl` | Package documentation on ocaml.org | `https://ocaml.org/p/lwt/latest/doc/index.html` |
| `packageUrl` | The package page that was scraped | `https://ocaml.org/p/lwt/latest` |
| `searchQuery` | The search query that found the package; null for `packages` input | `http` |
| `scrapedAt` | Time of the scrape (ISO 8601, UTC) | `2026-10-15T19:08:56.393Z` |
| `sourceUrl` | Page the record came from | `https://ocaml.org/p/lwt/latest` |

### How to use OCaml Packages Scraper

1. Open OCaml Packages Scraper in Apify Console and go to the Input tab.
2. Type one or more search queries (for example `yaml`, `http` or `author:"Jérôme Vouillon"`), or list packages by name or URL (for example `lwt`, `yojson@2.1.0` or `https://ocaml.org/p/yojson/2.1.0`). You can use both fields in the same run.
3. Set **Maximum results** to the number of packages you want in total.
4. Click **Start**. Each search query is paged through the ocaml.org package search, 50 results per page, and every result's package page is opened and parsed.
5. When the run finishes, open the Output tab, or export the dataset as JSON, CSV, Excel, XML or RSS.

Tips:

A bare package name always gives the latest version. To get an older release, use `name@version`, `name.version` or a full version URL such as `https://ocaml.org/p/yojson/2.1.0`; that record then has `isLatest` set to false.

`maxItems` is one budget shared by every target in the run. Packages listed under `packages` are scraped first, then the search queries in order, so a large first search can use the whole budget. If the same package version turns up twice (in two searches, or as a package and a search result), it is returned once.

The Actor keeps a modest pace (about two requests per second) so a run of 50 packages usually finishes in well under a minute.

### Input

| Field | Type | Required | Description |
|---|---|---|---|
| `searchQueries` | array of strings | One of the two | ocaml.org package search terms, free text or filters such as `author:"Name"`. Every result's package page is scraped. Use `*` to walk the whole package index (set `maxItems` to bound it). |
| `packages` | array of strings | One of the two | opam package names (`lwt`), `name@version` (`yojson@2.1.0`) or ocaml.org package URLs (`https://ocaml.org/p/yojson/2.1.0`). |
| `maxItems` | integer | No | Maximum number of packages across all targets, 1-10000, default 50. |
| `proxyConfiguration` | object | No | Proxy settings. Default: no proxy (ocaml.org works without one). |

Give at least one search query or one package. A search URL copied from ocaml.org (`https://ocaml.org/packages/search?q=yaml`) also works in either field.

```json
{
    "searchQueries": ["yaml", "author:\"Jérôme Vouillon\""],
    "packages": ["lwt", "https://ocaml.org/p/yojson/2.1.0"],
    "maxItems": 50,
    "proxyConfiguration": { "useApifyProxy": false }
}
```

### Output

Each package version is one item in the run's dataset. These two items come from a real run with `{"packages": ["lwt", "https://ocaml.org/p/yojson/2.1.0"]}` (the lwt description is shortened here):

```json
[
    {
        "id": "lwt@6.1.2",
        "name": "lwt",
        "version": "6.1.2",
        "isLatest": true,
        "synopsis": "Promises and event-driven I/O",
        "description": "A promise is a value that may become determined in the future.\n\nLwt provides typed, composable promises. ...",
        "license": "MIT",
        "homepage": "https://github.com/ocsigen/lwt",
        "opamFileUrl": "https://github.com/ocaml/opam-repository/blob/master/packages/lwt/lwt.6.1.2/opam",
        "versionsCount": 32,
        "authors": ["Jérôme Vouillon", "Jérémie Dimino"],
        "maintainers": ["Raphaël Proust <code@bnwr.net>", "Anton Bachin"],
        "sourceArchiveUrl": "https://github.com/ocsigen/lwt/archive/refs/tags/6.1.2.tar.gz",
        "checksums": [
            "md5=00bc7eb2772a28530f7365a7040f0c4f",
            "sha512=a4737a2fc44aaeedca46ec6134c07013d5cf44039a988a6e79a40715a53e43601d4c91bbf11a8e89540528d912b8fa487e3068465c136c1156a872258eb3443b"
        ],
        "publishedAt": "2026-04-29",
        "installCommand": "opam install lwt",
        "dependencies": [
            { "name": "ocplib-endian", "constraint": null },
            { "name": "dune-configurator", "constraint": null },
            { "name": "cppo", "constraint": "build & >= \"1.1\"" },
            { "name": "ocaml", "constraint": ">= \"4.14\"" },
            { "name": "dune", "constraint": ">= \"3.18\"" }
        ],
        "devDependencies": [
            { "name": "odoc", "constraint": "with-doc & >= \"2.3\"" },
            { "name": "ocamlfind", "constraint": "dev & >= \"1.7.3-1\"" }
        ],
        "dependenciesCount": 5,
        "usedByCount": 511,
        "docsUrl": "https://ocaml.org/p/lwt/latest/doc/index.html",
        "packageUrl": "https://ocaml.org/p/lwt/latest",
        "searchQuery": null,
        "scrapedAt": "2026-10-15T19:08:56.393Z",
        "sourceUrl": "https://ocaml.org/p/lwt/latest"
    },
    {
        "id": "yojson@2.1.0",
        "name": "yojson",
        "version": "2.1.0",
        "isLatest": false,
        "synopsis": "Yojson is an optimized parsing and printing library for the JSON format",
        "description": "Yojson is an optimized parsing and printing library for the JSON format.\n\nydump is a pretty-printing command-line program provided with the\nyojson package.",
        "license": "BSD-3-Clause",
        "homepage": "https://github.com/ocaml-community/yojson",
        "opamFileUrl": "https://github.com/ocaml/opam-repository/blob/master/packages/yojson/yojson.2.1.0/opam",
        "versionsCount": 15,
        "authors": ["Martin Jambon"],
        "maintainers": ["paul-elliot@tarides.com", "nathan@tarides.com", "marek@tarides.com"],
        "sourceArchiveUrl": "https://github.com/ocaml-community/yojson/releases/download/2.1.0/yojson-2.1.0.tbz",
        "checksums": [
            "sha256=9fcb1ff2db58ab259f9228796b0ada4794eae97177b1833371380c4e4f90b15d",
            "sha512=31ab8580e0e4e8a05459fbec2fb9424a3df4cf5aef6df21fc577dbe3d32e81103a632c6a511c4dcf1f51819e88b38d2ce7d6888545bc3f359e69e070f8d2e69c"
        ],
        "publishedAt": "2023-05-02",
        "installCommand": "opam install yojson.2.1.0",
        "dependencies": [
            { "name": "seq", "constraint": ">= \"0.2.2\"" },
            { "name": "cppo", "constraint": "build" },
            { "name": "ocaml", "constraint": ">= \"4.02.3\"" },
            { "name": "dune", "constraint": ">= \"2.7\"" }
        ],
        "devDependencies": [
            { "name": "odoc", "constraint": "with-doc" },
            { "name": "alcotest", "constraint": "with-test & >= \"0.8.5\"" }
        ],
        "dependenciesCount": 4,
        "usedByCount": 287,
        "docsUrl": "https://ocaml.org/p/yojson/2.1.0/doc/index.html",
        "packageUrl": "https://ocaml.org/p/yojson/2.1.0",
        "searchQuery": null,
        "scrapedAt": "2026-10-15T19:08:56.963Z",
        "sourceUrl": "https://ocaml.org/p/yojson/2.1.0"
    }
]
```

Fields that a package page doesn't show are `null` (single values) or an empty list (lists), so every item has the same shape. For example, some packages have neither a license link nor a README License paragraph, or have no description.

### How much does it cost to scrape ocaml.org?

OCaml Packages Scraper is charged per result. You pay only for the package records returned, and `maxItems` caps how many that can be. Searches that match nothing, unknown packages and failed pages cost nothing. See the Pricing tab for the current price.

### Use with AI agents (MCP)

Connect OCaml Packages Scraper to Claude, Cursor or any MCP client through the Apify MCP server:

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

Example prompts:

1. "Use the OCaml packages scraper to get lwt, cmdliner and yojson, and list each one's license, latest version and dependencies with constraints."
2. "Search ocaml.org for packages by author:"Jérôme Vouillon" and make a table of name, version, publish date and how many packages use each one."
3. "Find the 20 most relevant OCaml HTTP packages and tell me which have no license value or have not had a release since 2024."

An agent sees the input fields `searchQueries`, `packages` and `maxItems`, so it can search or look up exact packages in one call.

### Integrations and API

Run OCaml Packages 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
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("axiomworks/ocaml-packages-scraper").call(
    run_input={"packages": ["lwt", "yojson@2.1.0"], "searchQueries": ["yaml"], "maxItems": 50}
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["id"], item["license"], len(item["dependencies"]))
```

**JavaScript**

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('axiomworks/ocaml-packages-scraper').call({
    searchQueries: ['yaml'],
    maxItems: 50,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const p of items) {
    // p.name, p.version, p.isLatest, p.dependencies ...
}
```

**cURL**

```bash
curl -X POST "https://api.apify.com/v2/acts/axiomworks~ocaml-packages-scraper/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchQueries": ["yaml"], "maxItems": 20}'
```

The Integrations tab connects the Actor to Make, Zapier, n8n, Google Sheets, Slack and webhooks, so you can, for example, send a row to a spreadsheet for every package version in a weekly scheduled run.

### FAQ

**Is there an official ocaml.org JSON API for packages?**
Not for package metadata. The package pages are HTML, so this Actor reads them and returns the same data as structured JSON. Think of it as an OCaml package API you can call from code or from an AI agent.

**How do I get an older version of a package?**
Use `name@version` (for example `yojson@2.1.0`) or the version's URL (`https://ocaml.org/p/yojson/2.1.0`) in `packages`. A bare name and every search result give the latest version.

**Which search queries work?**
Anything the ocaml.org package search accepts: free text such as `json` or `http server`, and filters such as `author:"Anil Madhavapeddy"`. Non-ASCII names work as typed.

**Why did my run return 0 results?**
The search matched no packages, or the package name doesn't exist on ocaml.org (HTTP 404). The run still ends as succeeded, and the log and status message say which target found nothing. Check the spelling, or try the search on ocaml.org first.

**Why is `license` or `description` null for some packages?**
License uses the sidebar license link first, then the first sentence or short text of the paragraph immediately after the README License heading. Some package pages have neither source, or no description section. The Actor returns `null` instead of guessing. The opam file (`opamFileUrl`) is the place to check those packages by hand.

**How fresh is the data?**
Every run reads the live pages, so the data is as current as ocaml.org at the time of the run (`scrapedAt`). Schedule the Actor to track new releases.

**Do I need a proxy?**
Usually not. ocaml.org answers normal requests without one, so the default is no proxy. If you see HTTP 403 or 429 in the log, turn on Apify Proxy; blocked requests are retried on a new session automatically.

**How many packages can one run return?**
Up to 10,000 (`maxItems`). Large searches are paged 50 results at a time until the search runs out or `maxItems` is reached.

### Is it legal to scrape ocaml.org?

This Actor collects only publicly available package metadata that anyone can see on ocaml.org without logging in. Package pages list author and maintainer names and sometimes email addresses; if you store or process those, you are responsible for doing so lawfully (for example under GDPR) and for respecting the site's terms. This is not legal advice.

OCaml Packages Scraper is an independent tool. It is not affiliated with, endorsed by or sponsored by ocaml.org or the OCaml Software Foundation.

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

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

ocaml.org package search terms, one per line, e.g. 'yaml' or author:"Jérôme Vouillon". Each query is searched like the site's package search (free text or filters such as author:"Name"), and every result's package page is scraped for the latest version. Use "\*" to list the whole package index (combine with maxItems). Leave empty when you only use packages.

## `packages` (type: `array`):

opam package names or ocaml.org package URLs, one per line, e.g. 'lwt', 'yojson@2.1.0' or 'https://ocaml.org/p/yojson/2.1.0'. A bare name returns the latest version; a URL or name@version returns that exact version (isLatest false when it isn't the newest). Unknown packages are skipped with a warning.

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

Maximum number of packages to return across all search queries and packages together, 1-10000, default 50. Packages are processed first, then searches; the run stops as soon as this many results are saved, and you are charged only for results returned.

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

Proxy to use for requests to ocaml.org, default no proxy. Turn on Apify Proxy (datacenter first) only if runs get blocked; blocked requests are retried on a new proxy session.

## Actor input object example

```json
{
  "searchQueries": [
    "yaml"
  ],
  "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 = {
    "searchQueries": [
        "yaml"
    ],
    "maxItems": 5,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

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

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

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

```

## Python example

```python
from apify_client import ApifyClient

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

# Prepare the Actor input
run_input = {
    "searchQueries": ["yaml"],
    "maxItems": 5,
    "proxyConfiguration": { "useApifyProxy": False },
}

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

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

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

```

## CLI example

```bash
echo '{
  "searchQueries": [
    "yaml"
  ],
  "maxItems": 5,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call axiomworks/ocaml-packages-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,axiomworks/ocaml-packages-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/JZnwgyCQaJbgghBb0/builds/CDmwmZFRmXbmDedaQ/openapi.json
