# Chemical Compound Facts & Identifier Mapping (`grit-77/wikidata-compounds`) Actor

Chemical compound lookup using the Wikidata Action API. Supply QIDs or a bounded name list to export formulas, mass quantities and cross-database identifiers as JSON.

- **URL**: https://apify.com/grit-77/wikidata-compounds.md
- **Developed by:** [Grit](https://apify.com/grit-77) (community)
- **Categories:** AI, Open source
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.50 / 1,000 compounds

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

## Chemical Compound Facts & Identifier Mapping

Map explicit Wikidata chemical QIDs or a bounded list of chemical names to structured formulas, mass quantities and cross-database identifiers.

The actor uses the official [Wikidata Action API](https://www.wikidata.org/w/api.php?action=help\&modules=wbgetentities). Direct QIDs are fetched in batches of at most 50. The prefilled three-QID input fits in one request, excluding retries. Optional name lookup uses one [wbsearchentities](https://www.wikidata.org/w/api.php?action=help\&modules=wbsearchentities) search per name and one bounded candidate fetch, without pagination. It does not run SPARQL scans.

### What it returns

| Field | Type | Meaning |
|---|---|---|
| `qid` | string | Accepted Wikidata chemical entity ID |
| `label` | string or null | English compound label; no translation fallback |
| `formula` | string | P274 chemical formula, preserving Unicode subscripts |
| `molarMassValue` | string or null | P2067 source quantity, preserving decimal precision; leading `+` removed |
| `molarMassUnit` | string or null | Original P2067 unit URI, or `1` if the source is unitless |
| `casNumber` | string or null | P231 CAS number |
| `pubchemCid` | string or null | P662 PubChem compound identifier |
| `inchiKey` | string or null | P235 InChIKey |
| `smiles` | string or null | P233 canonical SMILES, falling back to P2017 isomeric SMILES |
| `sourceUrl` | string | Official Wikidata entity URL |
| `license` | string | `CC0-1.0`, the [structured-data license](https://www.wikidata.org/wiki/Wikidata:Licensing) |

`molarMassValue` and `molarMassUnit` preserve the source's [P2067 mass](https://www.wikidata.org/wiki/Property:P2067) statement. They do not calculate a molar mass or convert units. The included sample uses [dalton](https://www.wikidata.org/wiki/Q483261), so do not interpret its numeric values as measured g/mol. Missing optional properties are null. For multiple values, the first usable preferred statement is selected before normal statements; deprecated statements are ignored. This is a scalar identifier mapping rather than a complete list of alternate identifiers.

Accepted entities must be Wikidata items whose usable direct P31 classes are Q113145171 or Q11173, with a nonempty P274 formula. Entities with other P31 classes, including humans, are rejected. There is no subclass traversal. Exported compound rows contain the fields above. Raw claims, qualifiers, references, contributor details, revision history, prose descriptions and images are discarded.

### Input examples

Default / prefill:

```json
{"qids":["Q283","Q153","Q18216"],"maxItems":3}
```

Name lookup:

```json
{"qids":[],"names":["aspirin","ethanol"],"maxItems":2,"searchLimit":3,"concurrency":2}
```

| Input | Default | Limit / behavior |
|---|---|---|
| `qids` | Three sample QIDs when both selection fields are absent | Up to 500 entries; trim, uppercase, remove duplicate valid QIDs |
| `names` | Empty | Up to 10 nonempty names, 200 characters each; trim and remove exact duplicates |
| `maxItems` | 3 | 1–500 output rows, including errors; QIDs take priority over names |
| `searchLimit` | 3 | 1–5 candidates per name; first candidate passing the chemical gate |
| `concurrency` | 2 | 1–5 simultaneous API requests; direct batches are sequential |

An empty JSON object uses the sample QIDs. Explicit empty selection arrays fail with a clear message. Names-only programmatic input does not add default QIDs; clear the prefilled QID list in Apify Console. Unknown settings and invalid numeric settings fail the run before network work. Individual malformed QIDs produce error rows so valid siblings can continue.

### Actual output example

This row was produced by the real local actor. See [sample_output.json](sample_output.json) for water, ethanol and aspirin.

```json
{
  "qid": "Q283",
  "label": "water",
  "formula": "H₂O",
  "molarMassValue": "18.02",
  "molarMassUnit": "http://www.wikidata.org/entity/Q483261",
  "casNumber": "7732-18-5",
  "pubchemCid": "962",
  "inchiKey": "XLYOFNOQVPJJNP-UHFFFAOYSA-N",
  "smiles": "O",
  "sourceUrl": "https://www.wikidata.org/wiki/Q283",
  "license": "CC0-1.0"
}
```

### Errors and limits

An unsuccessful lookup writes a minimal row such as `{"qid":"Q42","error":"NOT_CHEMICAL"}`. No rejected label or arbitrary input text is echoed. A malformed QID or unsuccessful name lookup has `qid: null`.

| Error code | Meaning |
|---|---|
| `INVALID_QID` | Entry is not a valid QID |
| `NOT_FOUND` | Wikidata did not return that entity |
| `NOT_CHEMICAL` | Entity does not pass the direct chemical-class gate |
| `MISSING_FORMULA` | Eligible class but no usable P274 formula |
| `NO_CHEMICAL_MATCH` | No eligible compound among the bounded search candidates |
| `SOURCE_UNAVAILABLE` | Source request failed, returned a terminal API error, or exhausted retries |

Transient network failures, HTTP 429/5xx and transient API errors have at most three attempts, with exponential backoff and bounded Retry-After handling. Persistent failures become uncharged per-item errors. The compound spending limit is checked before work and before delivering compounds; after a charge reaches the limit, delivery stops.

The actor fetches only explicitly selected entities or bounded name candidates. Its output is community-maintained source data, with no independent validation of chemical values. Ambiguous names can identify an unintended compound; use QIDs for exact selection. Missing or differently classified chemicals may be rejected. There are no raw-response exports, images, Wikipedia articles or broad database crawls.

### Pricing proposal

`compound`: **$0.0015 per successfully saved compound** ($1.50 per 1,000 compound events). Error rows, duplicate results and entries excluded by `maxItems` do not trigger this event.

This proposed result price is 25% below the most used comparable chemical lookup found in the dated Store API searches: [cloud9_ai/pubchem-scraper](https://apify.com/cloud9_ai/pubchem-scraper), $0.002 per result. See [PRICING.md](PRICING.md) for the query scope, evidence and event configuration. Pricing must be configured by the owner before publication; the local actor does not configure Store monetization.

### FAQ

**Do I need a Wikidata API key?** No. These are public official API requests.

**Why is a known chemical rejected?** Only the two requested direct P31 classes and a usable P274 formula pass. Different classes or subclass-only membership do not pass.

**Why does a mass unit say dalton?** That is the source's original unit. The actor preserves it and does not silently relabel it as g/mol.

**Can I get all alternate CAS numbers or PubChem CIDs?** This version returns one selected value per requested property. Use the source URL to inspect alternatives.

**Is a failed lookup billed as a compound?** No. Only successful saved compounds trigger the `compound` event. Publication must avoid an additional automatic per-dataset-item event, which would charge errors too.

### Use with AI agents / MCP

When the owner publishes the actor, use Apify's [MCP server](https://docs.apify.com/platform/integrations/mcp) to run it with explicit QIDs or bounded chemical names, then retrieve its dataset. MCP lets an AI agent invoke the actor as a tool. Example task: “Look up Q283 and Q153, return their formula, CAS, PubChem CID and original mass unit, and retain the Wikidata source URL.” Treat nulls as unavailable and error codes as unsuccessful lookups. No text generation occurs inside this actor.

### Local development

Use the existing `C:/GritWork/earn/.venv/Scripts/python`; do not install into it. From this actor directory, set `APIFY_LOCAL_STORAGE_DIR` to its `storage` directory, put the default JSON in `storage/key_value_stores/default/INPUT.json`, and run:

```text
C:/GritWork/earn/.venv/Scripts/python -m src
C:/GritWork/earn/.venv/Scripts/python -m pytest -m "not live" -q -p no:cacheprovider
C:/GritWork/earn/.venv/Scripts/python -m pytest -m live -q -p no:cacheprovider
apify validate-schema
```

For PowerShell: `$env:APIFY_LOCAL_STORAGE_DIR = "$PWD/storage"`. Local runs have no real event charges until Store pay-per-event pricing is configured. [REPORT.md](REPORT.md) records the measured verification results and limitations.

# Actor input Schema

## `qids` (type: `array`):

Up to 500 explicit QIDs. Only direct P31 Q113145171 or Q11173 entities with P274 are accepted. Other classes are rejected. Duplicates are removed; malformed QIDs produce uncharged error codes. Clear this list for names only.

## `names` (type: `array`):

Optional: up to 10 English names, 200 characters each. One wbsearchentities request per name, no pagination. The first candidate passing the chemical gate is used. QIDs are more precise for ambiguous names.

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

Cap output rows, including error rows. Direct QIDs take priority over names. Duplicates are removed before the cap. Only successful compounds are charged.

## `searchLimit` (type: `integer`):

Maximum candidates checked per name. No continuation requests or broad entity scans.

## `concurrency` (type: `integer`):

Hard limit on simultaneous Wikidata requests. Direct QIDs use sequential batches of at most 50; optional name requests can overlap.

## Actor input object example

```json
{
  "qids": [
    "Q283",
    "Q153",
    "Q18216"
  ],
  "maxItems": 3,
  "searchLimit": 3,
  "concurrency": 2
}
```

# Actor output Schema

## `dataset` (type: `string`):

Dataset of chemical facts and minimal per-item error codes.

# 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 = {
    "qids": [
        "Q283",
        "Q153",
        "Q18216"
    ],
    "maxItems": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("grit-77/wikidata-compounds").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 = {
    "qids": [
        "Q283",
        "Q153",
        "Q18216",
    ],
    "maxItems": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("grit-77/wikidata-compounds").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 '{
  "qids": [
    "Q283",
    "Q153",
    "Q18216"
  ],
  "maxItems": 3
}' |
apify call grit-77/wikidata-compounds --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,grit-77/wikidata-compounds"
        }
    }
}
```

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/1g3GaF3RoHSHIVroL/builds/2ROYPI3plMp8asZyi/openapi.json
