# Conda Repodata Extractor (`datamule/conda-repodata-extractor`) Actor

Point at any Conda channel's repodata.json and get one structured row per package: name, version, build, subdir, license, md5/sha256, size, timestamp, depends and constrains.

- **URL**: https://apify.com/datamule/conda-repodata-extractor.md
- **Developed by:** [Datamule](https://apify.com/datamule) (community)
- **Categories:** Developer tools
- **Stats:** 3 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.35 / 1,000 packages

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Conda Repodata Extractor

**Point at any Conda channel's `repodata.json` and get one clean, structured row per package.**

Every Conda channel — [conda-forge](https://conda.anaconda.org/conda-forge), [bioconda](https://conda.anaconda.org/bioconda), [PyTorch](https://conda.anaconda.org/pytorch), [NVIDIA](https://conda.anaconda.org/nvidia), the Anaconda `pkgs/main` **defaults**, and the tens of thousands of self‑hosted, org and [anaconda.org](https://anaconda.org) channels — publishes the **identical `repodata.json`** index for each platform subdir. This actor speaks that one standard, so **a single actor spans every Conda channel in existence** — no per‑channel scraper, no host enumeration.

Give it a channel and a subdir; get back a flat, fully‑typed dataset row for every package build, ready for a spreadsheet, a database, a dependency‑graph, a license audit or an SBOM.

### What it does

- **Any channel, one input.** Pass a channel base (`https://conda.anaconda.org/pytorch`) + a `subdir`, or paste a full `.../<subdir>/repodata.json` URL directly. A compressed `.json.bz2` / `.json.zst` URL is normalized to the plain `.json`.
- **Both package maps, merged.** conda ships `.tar.bz2` builds under `packages` and the newer `.conda` builds under `packages.conda`. This actor merges **both** into one globally‑sorted stream — nothing is missed, nothing is double‑counted.
- **Memory‑flat streaming.** `repodata.json` for a big subdir (e.g. `conda-forge/linux-64`) is hundreds of megabytes. The feed is streamed to disk and parsed incrementally, so RAM stays flat regardless of channel size, and `maxRecords` stops early.
- **Deterministic under a cap.** Records are emitted in sorted filename order, and `repodata.json` is an immutable artifact — so a capped run always returns the same first *N* records.

### Input

| Field | Required | Description |
|---|---|---|
| `channelUrl` | ✅ | A conda channel base (`https://conda.anaconda.org/pytorch`) **or** a full `repodata.json` URL. |
| `subdir` | | Platform subdir used to complete a channel base. Default `noarch`. One of `noarch`, `linux-64`, `linux-aarch64`, `linux-ppc64le`, `osx-64`, `osx-arm64`, `win-64`, `win-arm64`. Ignored when `channelUrl` is already a full repodata URL. |
| `packageFilter` | | Case‑insensitive substring matched against each package **name**; non‑matching records are skipped. |
| `maxRecords` | | Global cap on emitted rows (each row = one package record = one billable event). |
| `bearer` | | Bearer token for a private/gated channel. Never logged. |
| `extraHeaders` | | Extra HTTP headers as a JSON object for gated channels. Never logged. |

#### Example inputs

```json
{ "channelUrl": "https://conda.anaconda.org/pytorch", "subdir": "noarch" }
```

```json
{ "channelUrl": "https://conda.anaconda.org/nvidia", "subdir": "linux-64", "packageFilter": "cuda", "maxRecords": 500 }
```

```json
{ "channelUrl": "https://repo.anaconda.com/pkgs/main/osx-arm64/repodata.json" }
```

### Output

One row per package record (from **both** the `.tar.bz2` and `.conda` maps):

| Column | Description |
|---|---|
| `filename` | The package filename (the repodata map key), e.g. `botorch-0.1.0-0.tar.bz2`. |
| `name` | Package name. |
| `version` | Package version. |
| `build` / `buildNumber` | Build string and build number. |
| `subdir` | Platform subdir. |
| `license` | Declared license (SPDX‑ish string, when present). |
| `md5` / `sha256` | Artifact checksums. |
| `size` | Artifact size in bytes. |
| `timestamp` | Build timestamp, normalized to ISO‑8601 UTC. |
| `depends` | Array of runtime dependency specs (`"pytorch >=1.1"`, …). |
| `constrains` | Array of optional‑constraint specs. |
| `track_features` | Feature flags (space‑delimited string), when present. |
| `_raw` | The complete original record — nothing is ever dropped (`license_family`, `noarch`, `platform`, …). |
| `_source` / `_subdir` / `_packageCount` | The exact repodata URL, the subdir, and the total record count. |

Every field is **nullable** and read by key presence — a channel that omits `license` / `depends` / `constrains` / `track_features` simply yields `null` for that column, never an error.

### Notes & scope

- **Public metadata only.** This reads a channel's published package index — it does **not** download the packages themselves.
- **GTFS‑style honesty.** An empty subdir, or a `packageFilter` that excludes everything, returns **0 records** and a clean success. A URL that isn't conda repodata (an HTML error page, a 404 body, JSON without a `packages` key) fails fast with a clear message.
- **Find channels** on [anaconda.org](https://anaconda.org). Common bases: `https://conda.anaconda.org/<channel>` and the Anaconda defaults at `https://repo.anaconda.com/pkgs/main`.

### Pricing

Pay‑per‑event: **$0.0005 per package record** returned. A capped run charges only for what it emits.

# Actor input Schema

## `channelUrl` (type: `string`):

A conda channel base OR a full repodata.json URL. Every conda channel publishes the identical repodata standard, so one actor works against any of them: https://conda.anaconda.org/pytorch (channel base — completed with the subdir below), https://conda.anaconda.org/bioconda, https://conda.anaconda.org/nvidia, https://conda.anaconda.org/conda-forge, https://repo.anaconda.com/pkgs/main (Anaconda defaults), or any self-hosted / org / anaconda.org channel. You can also paste a full URL like https://conda.anaconda.org/pytorch/noarch/repodata.json. A compressed .json.bz2 / .json.zst URL is rewritten to the plain .json.

## `subdir` (type: `string`):

The platform subdir to read when channelUrl is a channel BASE (ignored when channelUrl already points at a repodata.json). Common values: noarch (default, cross-platform packages), linux-64, linux-aarch64, osx-64, osx-arm64, win-64. Each subdir has its own repodata.json.

## `packageFilter` (type: `string`):

Optional case-insensitive substring matched against each record's package name; records whose name does not contain it are skipped. Leave empty to extract every package in the subdir. Example: torch (matches pytorch, botorch, torchvision, …).

## `maxRecords` (type: `integer`):

A GLOBAL cap on the number of package-record rows to emit (each row is one package record and one billable event). Records are emitted in globally-sorted filename order and repodata.json is an immutable file, so a small cap is deterministic. Leave empty to emit every record — note a big channel subdir (e.g. conda-forge/linux-64) can hold hundreds of thousands of records.

## `bearer` (type: `string`):

Optional bearer token for a private/gated conda channel (sent as Authorization: Bearer \*\*\*). Not required for public channels. Never logged.

## `extraHeaders` (type: `object`):

Optional extra HTTP headers as a JSON object, e.g. {"x-api-key": "..."} for gated/authenticated channels. Not required for public channels. Header values are never logged.

## Actor input object example

```json
{
  "channelUrl": "https://conda.anaconda.org/pytorch",
  "subdir": "noarch",
  "maxRecords": 25
}
```

# 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 = {
    "channelUrl": "https://conda.anaconda.org/pytorch",
    "maxRecords": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("datamule/conda-repodata-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 = {
    "channelUrl": "https://conda.anaconda.org/pytorch",
    "maxRecords": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("datamule/conda-repodata-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 '{
  "channelUrl": "https://conda.anaconda.org/pytorch",
  "maxRecords": 25
}' |
apify call datamule/conda-repodata-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,datamule/conda-repodata-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/VyHsQAf5itQK0xJVc/builds/R0kb2NCUwp36V0usH/openapi.json
