Overview
protocols.io hosts versioned, citable scientific protocols (each published version gets a DOI under 10.17504/protocols.io.*), plus team workspaces, experiment run records, and a file manager. It offers two programmatic routes:
- Official MCP server —
https://www.protocols.io/mcp(Streamable HTTP; OAuth 2.0 sign-in or a Bearer client token). It is also listed in Claude's connector directory as "protocols.io". - REST API — base
https://www.protocols.io/api, split across versions: most endpoints arev3, while getting/updating protocols, protocol steps, run records, file-manager search, and organization exports usev4. The API reference is https://apidoc.protocols.io/.
When to Use This Skill
- Searching protocols.io for protocols by keyword, author, or workspace, or resolving a protocol from its DOI or URI
- Reading a protocol's steps and materials (as Markdown, HTML, or Draft.js JSON) to analyse, adapt, or compare it
- Creating a private protocol, filling in its metadata and steps, and publishing it (or reserving a DOI) for citation in a paper
- Reading or posting protocol comments and step discussions
- Listing or joining workspaces, searching workspace files, uploading files, and exporting an organization's content
- Creating and updating experiment run records for a protocol
Does NOT Trigger
| Scenario | Use Instead |
|---|---|
| Recording results in a general electronic lab notebook (entries, attachments, notebook backups) | alterlab-labarchive (LabArchives) or alterlab-benchling (Benchling) |
| Turning a written protocol into a script for an Opentrons OT-2/Flex or a Hamilton/Tecan liquid handler | alterlab-opentrons or alterlab-pylabrobot |
| Depositing a preprint or choosing a data/code repository (bioRxiv, OSF, Zenodo) | alterlab-preprint-deposition or alterlab-open-science |
| Writing or polishing a manuscript's Methods section | alterlab-scientific-writing |
Prefer the MCP Server When It Is Connected
If protocols.io tools are available in the session (connector or MCP server), use them for search, retrieval, and editing: they run on the user's own account, handle OAuth, and return live data. Fall back to the REST calls below for anything the tools do not cover, or when no connector is attached. To connect one, add https://www.protocols.io/mcp as a remote MCP server (or add "protocols.io" from Claude's connector directory) and sign in. Either way, cite the protocol DOI and the retrieval date when reporting protocol content.
Authentication
| Token | Where it comes from | Reaches |
|---|---|---|
CLIENT_ACCESS_TOKEN |
https://www.protocols.io/developers | Public content + the private content of the user who created the client |
OAUTH_ACCESS_TOKEN |
OAuth 2.0 authorization-code flow | Public content + the authorizing user's private content |
Send it on every request as Authorization: Bearer <token>, and keep it in an environment variable (for example PROTOCOLS_IO_TOKEN), never in code or notebooks. OAuth tokens last about a year; the API warns a month before expiry and a refresh invalidates the old token pair. The authorization link, token exchange, and refresh calls are in references/authentication.md.
Request Basics
Identifiers. Protocol endpoints accept an integer id, the protocol uri (slug such as tree-mapping-for-leaf-collection-megantic-only-baaciaaw), and — for the v4 get/steps endpoints — the DOI (10.17504/protocols.io.baaciaaw or protocols.io.baaciaaw). Append /v1 for a specific version or /latest for the newest one. Write endpoints also accept the protocol guid.
Content format. v4 protocol, steps, and record reads take content_format=json|html|markdown for rich-text fields (description, guidelines, warnings, materials text, step text). json is Draft.js; request markdown when the text will be read or summarised.
Pagination. List endpoints take page_id (starting at 1) and page_size (1–100, default 10) and return a pagination object.
Errors are in the body. Every response carries a JSON status_code, where 0 means success. Failures usually arrive as HTTP 400 with error_message (v3) or status_text (v4) — a missing or bad token is 1218, an expired OAuth token 1219 — so check status_code, not only the HTTP status.
Rate limits. 100 requests per minute per user; the PDF endpoint (/view/<uri>.pdf) allows 5 per minute signed in and 3 per minute signed out. Exceeding a limit returns HTTP 429.
Endpoint Map
| Task | Call | Details |
|---|---|---|
| Search protocols | GET /v3/protocols?filter=public&key=... |
references/protocols_api.md |
| A researcher's or workspace's protocols | GET /v3/researchers/<username>/protocols, GET /v3/workspaces/<uri>/protocols |
protocols_api.md, workspaces.md |
| Get a protocol (with steps and materials) | GET /v4/protocols/<id-uri-or-doi> |
protocols_api.md |
| Get steps / materials | GET /v4/protocols/<id>/steps, GET /v3/protocols/<id>/materials |
protocols_api.md |
| Create a protocol | POST /v3/protocols/<new-guid> |
protocols_api.md |
| Update metadata | PUT /v4/protocols/<id> (JSON) |
protocols_api.md |
| Add, change, or delete steps | POST / DELETE /v4/protocols/<id>/steps |
protocols_api.md |
| Publish or reserve a DOI | POST /v3/protocols/<uri>/publish (prepublish=1 reserves) |
protocols_api.md |
| Bookmark | POST / DELETE /v3/protocols/<uri>/bookmarks |
protocols_api.md |
GET https://www.protocols.io/view/<id-or-uri>.pdf |
protocols_api.md |
|
| Recently published | GET /v3/publications?latest=N or ?from=&to= |
protocols_api.md |
| Comments and step discussions | /v3/protocols/<uri>/comments, /v3/steps/<step_id>/discussions |
references/discussions.md |
| Workspaces and membership | /v3/workspaces, /v3/workspaces/<uri>/members |
references/workspaces.md |
| File-manager search, trash, uploads | /v4/filemanager/.../search, /v3/filemanager/trash, /v3/files |
references/file_manager.md |
| Profile, run records, notifications, org export | /v3/session/profile, /v3/records, /v3/researchers/notifications, /v4/organizations/<uri>/content/exports |
references/additional_features.md |
All paths are relative to https://www.protocols.io/api except the PDF view. https://protocols.io/... redirects to the www. host; there is no api.protocols.io host.
Python Examples
The examples use requests and one helper that turns protocols.io's in-body errors into exceptions:
import os
import time
import uuid
import requests
BASE = "https://www.protocols.io/api"
HEADERS = {"Authorization": f"Bearer {os.environ['PROTOCOLS_IO_TOKEN']}"}
def call(method, path, retries=3, **kwargs):
"""Send a request; back off on HTTP 429 and raise when the JSON status_code is not 0."""
for attempt in range(retries):
resp = requests.request(method, f"{BASE}{path}", headers=HEADERS, timeout=60, **kwargs)
if resp.status_code == 429 and attempt < retries - 1:
time.sleep(int(resp.headers.get("Retry-After", 30 * (attempt + 1))))
continue
try:
data = resp.json()
except ValueError: # e.g. an HTML error page from a proxy
resp.raise_for_status()
raise
if data.get("status_code", 0) != 0:
message = data.get("error_message") or data.get("status_text")
raise RuntimeError(f"protocols.io {data.get('status_code')}: {message} (HTTP {resp.status_code})")
return data
raise RuntimeError("protocols.io rate limit: retries exhausted")
Search and cite
found = call("GET", "/v3/protocols", params={
"filter": "public", "key": "CRISPR", "order_field": "relevance", "page_size": 10,
})
for item in found["items"]:
doi = (item.get("doi") or "").removeprefix("dx.doi.org/") # list items use a dx.doi.org/ prefix
print(item["title"], f"https://doi.org/{doi}" if doi else "(no DOI yet)")
Put the search phrase in double quotes inside key for an exact-match search.
Read a protocol by DOI, as Markdown
data = call("GET", "/v4/protocols/10.17504/protocols.io.baaciaaw",
params={"content_format": "markdown"})
protocol = data.get("payload") or data.get("protocol") # current v4 responses use "payload"
print(protocol["title"], protocol["url"])
for number, step in enumerate(protocol.get("steps", []), start=1):
print(number, step.get("step"))
Create, fill in, and publish a protocol
guid = uuid.uuid4().hex.upper() # new GUID without dashes, generated client-side
created = call("POST", f"/v3/protocols/{guid}", data={"type_id": 1}) # 1 protocol, 3 collection, 4 document
uri = created["protocol"]["uri"]
call("PUT", f"/v4/protocols/{guid}", json={
"title": "CRISPR-Cas9 knockout in HEK293T cells",
"description": "RNP electroporation workflow with T7E1 validation.",
"materials_text": "SpCas9 nuclease; synthetic sgRNA; Neon electroporation kit",
"status_id": 2, # 1 working, 2 still optimizing, 3 could not get it to work
})
# Steps are ordered by previous_guid: the first step has previous_guid None.
texts = ["Anneal and complex sgRNA with Cas9 (10 min, room temperature).",
"Electroporate 2e5 cells per reaction.",
"Harvest genomic DNA after 72 h and run the T7E1 assay."]
steps, previous = [], None
for text in texts:
step_guid = uuid.uuid4().hex.upper()
steps.append({"guid": step_guid, "previous_guid": previous, "step": text})
previous = step_guid
call("POST", f"/v4/protocols/{guid}/steps", json={"steps": steps})
# Reserve a DOI without making it public (drop prepublish to publish openly).
call("POST", f"/v3/protocols/{uri}/publish", params={"prepublish": 1})
Publishing needs a title and at least one author, and a version with a DOI can no longer be edited, so review the protocol in the web editor first. Published protocols accept only a subset of PUT fields (keywords, disclaimer, ethics statement, manuscript citation, references, funders, and the status/warning flags); content changes need a new version, which the API does not document, so create it in the web editor.
Upload a file
Uploads are three calls: register the file, send the bytes to S3 with the returned policy, then confirm:
path = "results.csv"
prep = call("POST", "/v3/files", data={"filename": os.path.basename(path)})
form, meta = prep["formData"], prep["metaData"]
fields = {"key": form["key"], "acl": form["acl"], "AWSAccessKeyId": form["AWSAccessKeyId"],
"Policy": form["Policy"], "Signature": form["Signature"], "Content-Type": form["ContentType"]}
with open(path, "rb") as fh: # the file must be the last form field
s3 = requests.post(f"https://{form['s3_bucket']}.s3.amazonaws.com/", data=fields,
files={"file": (os.path.basename(path), fh)}, timeout=300)
s3.raise_for_status()
call("PUT", f"/v3/files/{meta['file_id']}") # verify the upload
The documented upload call takes no folder, workspace, or tag fields. Attach the returned file_id to a collection (collection_items with content_type_id 15) or organise it in the web app. The S3 step follows the standard S3 POST-policy form, which the API docs do not spell out; try it with a small file first. See references/file_manager.md.
Common Workflows
Import and analyse an existing protocol. Search (GET /v3/protocols), fetch the chosen one as Markdown (GET /v4/protocols/<doi>), read its comments (GET /v3/protocols/<uri>/comments) for reported fixes, and record the DOI and version you used.
Draft and publish a lab protocol. Create (POST /v3/protocols/<guid>), set metadata (PUT /v4/protocols/<guid>), add steps in order (POST /v4/protocols/<guid>/steps), review with co-authors in the web editor, then reserve a DOI with prepublish=1 while the paper is under review and publish when it is accepted.
Document protocol runs. Create a run record from the protocol (POST /v3/records with protocol_uri), then check off steps and add notes with PUT /v3/records/<guid> (see references/additional_features.md). Store raw data in your ELN or repository and cite the record or protocol there.
Team workspace. List the user's workspaces (GET /v3/workspaces?filter=my_groups), list or search a workspace's protocols and files, and request to join a public workspace with POST /v3/workspaces/<uri>/members (see references/workspaces.md).
Institutional archive. On an organization account, start a full content export (POST https://<subdomain>.protocols.io/api/v4/organizations/<org_uri>/content/exports) and poll it until download_link is set.
Citing Protocols
Cite the version you actually used: Author(s). Title. protocols.io. https://doi.org/10.17504/protocols.io.<id>, keeping the version suffix (/v1, /v2, ...) when the DOI carries one. A version with a DOI cannot be edited, but /latest follows new versions, so pin the version in methods sections and data records.
Troubleshooting
| Symptom | Likely cause and fix |
|---|---|
HTTP 400 with status_code 1218 |
Missing or malformed Authorization: Bearer <token> header |
status_code 1219 "token is expired" |
Refresh the OAuth token (grant_type=refresh_token); the old pair stops working |
| Private protocol returns not found or 132 access denied | Token belongs to a user without access; use an OAuth token for the owning user or ask to be added to the workspace |
| 1905 on create | Workspace subscription limit reached |
| Step POST rejected ("loop detected", "multiple first steps", "steps are not forming a complete sequence") | Fix previous_guid chaining; when inserting a step, also resend the following step with its new previous_guid |
| 255 / 256 / 257 on publish | Already public / missing title / missing author |
| HTTP 429 | Over 100 requests per minute (or 5 PDF requests per minute); back off and cache reads |
Mac curl prints binary output |
Add --compressed |
Reference Files
references/authentication.md— token types, OAuth authorization link, token exchange and refresh, MCP connection, limitsreferences/protocols_api.md— search, get, create, update, steps, materials, publish, bookmarks, PDF, publications, reagentsreferences/discussions.md— protocol comments, step discussions, run-record comments, direct messagesreferences/workspaces.md— workspace lists, details, membership, workspace protocolsreferences/file_manager.md— file-manager search, trash and restore, S3 file uploadsreferences/additional_features.md— profile, run records, notifications, organization content export
Part of the AlterLab Academic Skills suite.