references/citation.bib
@ARTICLE{Dunham2012,
ABSTRACT = {The human genome encodes the blueprint of life, but the function of the vast majority of its nearly three billion bases is unknown. The Encyclopedia of DNA Elements (ENCODE) project has systematically mapped regions of transcription, transcription factor association, chromatin structure and histone modification. These data enabled us to assign biochemical functions for 80{\%} of the genome, in particular outside of the well-studied protein-coding regions. Many discovered candidate regulatory elements are physically associated with one another and with expressed genes, providing new insights into the mechanisms of gene regulation. The newly identified elements also show a statistical correspondence to sequence variants linked to human disease, and can thereby guide interpretation of this variation. Overall, the project provides new insights into the organization and regulation of our genes and genome, and is an expansive resource of functional annotations for biomedical research.},
AUTHOR = {Dunham, Ian and Kundaje, Anshul and Aldred, Shelley F. and Collins, Patrick J. and Davis, Carrie A. and Doyle, Francis and Epstein, Charles B. and Frietze, Seth and Harrow, Jennifer and Kaul, Rajinder and Khatun, Jainab and Lajoie, Bryan R. and Landt, Stephen G. and Lee, Bum-Kyu and Pauli, Florencia and Rosenbloom, Kate R. and Sabo, Peter and Safi, Alexias and Sanyal, Amartya and Shoresh, Noam and Simon, Jeremy M. and Song, Lingyun and Trinklein, Nathan D. and Altshuler, Robert C. and Birney, Ewan and Brown, James B. and Cheng, Chao and Djebali, Sarah and Dong, Xianjun and Ernst, Jason and Furey, Terrence S. and Gerstein, Mark and Giardine, Belinda and Greven, Melissa and Hardison, Ross C. and Harris, Robert S. and Herrero, Javier and Hoffman, Michael M. and Iyer, Sowmya and Kellis, Manolis and Kheradpour, Pouya and Lassmann, Timo and Li, Qunhua and Lin, Xinying and Marinov, Georgi K. and Merkel, Angelika and Mortazavi, Ali and Parker, Stephen C. J. and Reddy, Timothy E. and Rozowsky, Joel and Schlesinger, Felix and Thurman, Robert E. and Wang, Jie and Ward, Lucas D. and Whitfield, Troy W. and Wilder, Steven P. and Wu, Weisheng and Xi, Hualin S. and Yip, Kevin Y. and Zhuang, Jiali and Bernstein, Bradley E. and Green, Eric D. and Gunter, Chris and Snyder, Michael and Pazin, Michael J. and Lowdon, Rebecca F. and Dillon, Laura A. L. and Adams, Leslie B. and Kelly, Caroline J. and Zhang, Julia and Wexler, Judith R. and Good, Peter J. and Feingold, Elise A. and Crawford, Gregory E. and Dekker, Job and Elnitski, Laura and Farnham, Peggy J. and Giddings, Morgan C. and Gingeras, Thomas R. and Guigó, Roderic and Hubbard, Timothy J. and Kent, W. James and Lieb, Jason D. and Margulies, Elliott H. and Myers, Richard M. and Stamatoyannopoulos, John A. and Tenenbaum, Scott A. and Weng, Zhiping and White, Kevin P. and Wold, Barbara and Yu, Yanbao and Wrobel, John and Risk, Brian A. and Gunawardena, Harsha P. and Kuiper, Heather C. and Maier, Christopher W. and Xie, Ling and Chen, Xian and Mikkelsen, Tarjei S. and Gillespie, Shawn and Goren, Alon and Ram, Oren and Zhang, Xiaolan and Wang, Li and Issner, Robbyn and Coyne, Michael J. and Durham, Timothy and Ku, Manching and Truong, Thanh and Eaton, Matthew L. and Dobin, Alex and Tanzer, Andrea and Lagarde, Julien and Lin, Wei and Xue, Chenghai and Williams, Brian A. and Zaleski, Chris and Röder, Maik and Kokocinski, Felix and Abdelhamid, Rehab F. and Alioto, Tyler and Antoshechkin, Igor and Baer, Michael T. and Batut, Philippe and Bell, Ian and Bell, Kimberly and Chakrabortty, Sudipto and Chrast, Jacqueline and Curado, Joao and Derrien, Thomas and Drenkow, Jorg and Dumais, Erica and Dumais, Jackie and Duttagupta, Radha and Fastuca, Megan and Fejes-Toth, Kata and Ferreira, Pedro and Foissac, Sylvain and Fullwood, Melissa J. and Gao, Hui and Gonzalez, David and Gordon, Assaf and Howald, Cédric and Jha, Sonali and Johnson, Rory and Kapranov, Philipp and King, Brandon and Kingswood, Colin and Li, Guoliang and Luo, Oscar J. and Park, Eddie and Preall, Jonathan B. and Presaud, Kimberly and Ribeca, Paolo and Robyr, Daniel and Ruan, Xiaoan and Sammeth, Michael and Sandhu, Kuljeet Singh and Schaeffer, Lorain and See, Lei-Hoon and Shahab, Atif and Skancke, Jorgen and Suzuki, Ana Maria and Takahashi, Hazuki and Tilgner, Hagen and Trout, Diane and Walters, Nathalie and Wang, Huaien and Hayashizaki, Yoshihide and Reymond, Alexandre and Antonarakis, Stylianos E. and Hannon, Gregory J. and Ruan, Yijun and Carninci, Piero and Sloan, Cricket A. and Learned, Katrina and Malladi, Venkat S. and Wong, Matthew C. and Barber, Galt P. and Cline, Melissa S. and Dreszer, Timothy R. and Heitner, Steven G. and Karolchik, Donna and Kirkup, Vanessa M. and Meyer, Laurence R. and Long, Jeffrey C. and Maddren, Morgan and Raney, Brian J. and Grasfeder, Linda L. and Giresi, Paul G. and Battenhouse, Anna and Sheffield, Nathan C. and Showers, Kimberly A. and London, Darin and Bhinge, Akshay A. and Shestak, Christopher and Schaner, Matthew R. and Ki Kim, Seul and Zhang, Zhuzhu Z. and Mieczkowski, Piotr A. and Mieczkowska, Joanna O. and Liu, Zheng and McDaniell, Ryan M. and Ni, Yunyun and Rashid, Naim U. and Kim, Min Jae and Adar, Sheera and Zhang, Zhancheng and Wang, Tianyuan and Winter, Deborah and Keefe, Damian and Iyer, Vishwanath R. and Zheng, Meizhen and Wang, Ping and Gertz, Jason and Vielmetter, Jost and Partridge, E. and Varley, Katherine E. and Gasper, Clarke and Consortium, The ENCODE Project},
URL = {https://doi.org/10.1038/nature11247},
DATE = {2012-09-01},
DOI = {10.1038/nature11247},
ISSN = {1476-4687},
JOURNALTITLE = {Nature},
NUMBER = {7414},
PAGES = {57--74},
TITLE = {An integrated encyclopedia of DNA elements in the human genome},
VOLUME = {489},
}
@ARTICLE{Consortium2020,
AUTHOR = {{ENCODE Project Consortium}},
DATE = {2020},
DOI = {10.1038/s41586-020-2493-4},
JOURNALTITLE = {Nature},
NUMBER = {7818},
PAGES = {699--710},
TITLE = {Expanded encyclopaedias of DNA elements in the human and mouse genomes},
VOLUME = {583},
}
@ARTICLE{Luo2020,
ABSTRACT = {The Encyclopedia of DNA Elements (ENCODE) is an ongoing collaborative research project aimed at identifying all the functional elements in the human and mouse genomes. Data generated by the ENCODE consortium are freely accessible at the ENCODE portal (https://www.encodeproject.org/), which is developed and maintained by the ENCODE Data Coordinating Center (DCC). Since the initial portal release in 2013, the ENCODE DCC has updated the portal to make ENCODE data more findable, accessible, interoperable and reusable. Here, we report on recent updates, including new ENCODE data and assays, ENCODE uniform data processing pipelines, new visualization tools, a dataset cart feature, unrestricted public access to ENCODE data on the cloud (Amazon Web Services open data registry, https://registry.opendata.aws/encode-project/) and more comprehensive tutorials and documentation.},
AUTHOR = {Luo, Yunhai and Hitz, Benjamin C and Gabdank, Idan and Hilton, Jason A and Kagda, Meenakshi S and Lam, Bonita and Myers, Zachary and Sud, Paul and Jou, Jennifer and Lin, Khine and Baymuradov, Ulugbek K and Graham, Keenan and Litton, Casey and Miyasato, Stuart R and Strattan, J Seth and Jolanki, Otto and Lee, Jin-Wook and Tanaka, Forrest Y and Adenekan, Philip and O’Neill, Emma and Cherry, J Michael},
URL = {https://doi.org/10.1093/nar/gkz1062},
DATE = {2020-01},
DOI = {10.1093/nar/gkz1062},
EPRINT = {https://academic.oup.com/nar/article-pdf/48/D1/D882/32874907/gkz1062.pdf},
ISSN = {0305-1048},
JOURNALTITLE = {Nucleic Acids Research},
NUMBER = {D1},
PAGES = {D882--D889},
TITLE = {New developments on the Encyclopedia of DNA Elements (ENCODE) data portal},
VOLUME = {48},
}
references/graphql_schema.md
# ENCODE SCREEN GraphQL API Schema Reference
This document outlines the queries and types available in the ENCODE SCREEN
GraphQL API (`https://factorbook.api.wenglab.org/graphql`), as used in the
`encode-database` skill.
## Core Queries
### `cCRESCREENSearch`
Searches for candidate cis-Regulatory Elements (cCREs).
**Arguments:**
- `assembly` (String!): The genome assembly (e.g., "grch38", "mm10").
- `coordinates` ([GenomicRangeInput!]): List of `{chromosome, start, end}`
objects.
- `accessions` ([String!]): List of specific cCRE accessions.
- `rank_ctcf_start` / `rank_ctcf_end` (Float): Range filters for CTCF z-score.
- `cellType` (String): Filter by biosample-specific epigenetic signal.
**Returns Fields:**
- `chrom`, `start`, `len`, `pct`
- `ctcf_zscore`, `dnase_zscore`, `atac_zscore`, `enhancer_zscore`,
`promoter_zscore`
- `info { accession }`
- `nearestgenes { gene, distance }`
- `ctspecific { ct, ctcf_zscore, dnase_zscore, h3k4me3_zscore,`
`h3k27ac_zscore, atac_zscore }`
### `ccREBiosampleQuery`
Retrieves biosample metadata.
**Arguments:**
- `assembly` (String!)
**Returns Fields:**
- `biosamples` (List):
- `name`, `ontology`, `lifeStage`, `sampleType`, `displayname`
- Experiment and file accessions for assays (DNase, H3K4me3, H3K27ac,
CTCF, ATAC)
- `cCREZScores(accession: String!) { score, assay,` `experiment_accession
}`
### `cCREQuery`
Retrieves details for a specific cCRE.
**Arguments:**
- `assembly` (String!)
- `accession` (String!)
- `coordinates` (GenomicRangeInput)
**Returns Fields:**
- `accession`, `group`, `coordinates { chromosome, start, end }`
- `maxZ(assay: String!)`: Max Z-score for a given assay.
### `gene` / `snpQuery`
Retrieves nearby genes and SNPs.
**`gene` Arguments:** `chromosome`, `start`, `end`, `assembly`
**`snpQuery` Arguments:** `coordinates`, `assembly`, `common`
**Returns Fields:** Coordinates, transcripts, names, etc.
### `orthologQuery`
Retrieves orthologous cCREs in another assembly.
**Arguments:**
- `assembly` (String!)
- `accession` (String!)
**Returns Fields:**
- `ortholog { stop, start, chromosome, accession }`
### `linkedGenesQuery`
Retrieves linked genes (e.g., via HiC, eQTLs, CRISPR).
**Arguments:**
- `assembly` (String!)
- `accession` ([String]!)
**Returns Fields:**
- `gene`, `method`, `effectsize`, `assay`, `celltype`, `score`, `p_val`, etc.
### `entexQuery` / `entexActiveAnnotationsQuery`
Retrieves ENTEx data.
**`entexQuery` Arguments:** `accession` (String!)
**`entexActiveAnnotationsQuery` Arguments:** `coordinates` (GenomicRangeInput!)
**Returns Fields:**: Tissue, assay score, hap counts, allele ratio, p-values.
### `gene`
Resolves a gene name to its Ensembl ID and coordinates.
**Arguments:**
- `assembly` (String!): The genome assembly (e.g., "grch38").
- `name` ([String!]): List of gene symbols to look up.
**Returns Fields:**
- `name`, `id` (Ensembl gene ID), `coordinates { start, chromosome, end }`
### `gene_quantification`
Retrieves per-experiment gene expression quantification data.
**Arguments:**
- `assembly` (String!): The genome assembly.
- `gene_id_prefix` ([String]): Ensembl gene ID prefixes to filter by.
- `sortByTpm` (Boolean): If true, results are sorted by TPM descending.
- `limit` (Int): Maximum number of results to return.
**Returns Fields:**
- `experiment_accession`, `file_accession`, `tpm`, `fpkm`, `len`,
`effective_len`, `expected_count`, `pme_tpm`, `pme_fpkm`
### `gene_dataset`
Retrieves biosample metadata for RNA-seq experiments.
**Arguments:**
- `accession` ([String]): Filter by experiment accession(s).
- `tissue`, `biosample`, `biosample_type`, `cell_compartment`,
`assay_term_name` ([String]): Optional biosample filters.
- `processed_assembly` (String): Assembly filter (e.g., "GRCh38").
**Returns Fields:**
- `accession`, `biosample`, `tissue`, `biosample_type`, `cell_compartment`,
`assay_term_name`
### GWAS Queries
Queries for Genome-Wide Association Studies data.
**`getAllGwasStudies`:** Returns study name, author, pubmed ID.
**`getSNPsforGWASStudies(study: [String!]!)`:** Returns SNPs, ldblocks, rsquare,
coordinates. **`getGWASCtEnrichmentQuery(study: String!)`:** Returns celltype
enrichment data (fc, fdr, pvalue).
## Notes
**Pagination / Limits:** If a query is too large, it may return an error. Split
the request coordinates or accessions into smaller chunks. **Composability:**
Multiple queries can be batched in a single request, but it's often more
efficient to use the provided python script abstractions unless a highly
specific custom GraphQL query is required.
references/json_output_structure.md
# JSON Output Structure Reference
The python script `screen_api.py` saves the raw GraphQL response to a temporary
JSON file. The top-level structure is always:
```json
{
"data": {
"<QueryName>": <ResultArrayOrObject>
}
}
```
Below are the standard structures for each command to help you craft efficient
`jq` or `python` parsing queries:
## 1. `search` (`cCRESCREENSearch`)
Returns an array of cCRE objects.
```json
{
"data": {
"cCRESCREENSearch": [
{
"chrom": "<string>", "start": "<int>", "len": "<int>",
"pct": "<string>", // Classification: PLS, pELS, dELS, etc.
"ctcf_zscore": "<float>", "dnase_zscore": "<float>",
"atac_zscore": "<float>", "enhancer_zscore": "<float>",
"promoter_zscore": "<float>", "info": { "accession": "<string>" },
"ctspecific": [
// Only present if --cellType was specified
{
"ct": "<string>",
"ctcf_zscore": "<float>",
"dnase_zscore": "<float>",
"h3k4me3_zscore": "<float>",
"h3k27ac_zscore": "<float>",
"atac_zscore": "<float>"
}
]
}
]
}
}
```
## 2. `nearby-genes` (`cCRESCREENSearch`)
```json
{
"data": {
"cCRESCREENSearch": [
{
"chrom": "<string>", "start": "<int>", "len": "<int>",
"pct": "<string>", "info": { "accession": "<string>" },
"nearestgenes": [
{ "gene": "<string>", "distance": "<int>" }
]
}
]
}
}
```
## 3. `details` (`cCREQuery` and `ccREBiosampleQuery`)
```json
{
"data": {
"cCREQuery": [
{
"accession": "<string>", "group": "<string>",
"dnase": "<float>", "h3k4me3": "<float>", "h3k27ac": "<float>",
"ctcf": "<float>", "atac": "<float>", "coordinates":
{ "chromosome": "<string>", "start": "<int>", "end": "<int>" }
}
],
"ccREBiosampleQuery": {
"biosamples": [
{
"sampleType": "<string>",
"name": "<string>",
"ontology": "<string>",
"displayname": "<string>",
"dnase_acc": "<string|null>",
"h3k4me3_acc": "<string|null>",
"h3k27ac_acc": "<string|null>",
"ctcf_acc": "<string|null>",
"is_type_a": "<bool>",
"cCREZScores": [
{
"score": "<float>",
"assay": "<string>",
"experiment_accession": "<string>"
}
]
}
]
}
}
}
```
## 4. `biosamples` (`ccREBiosampleQuery`)
```json
{
"data": {
"ccREBiosampleQuery": {
"biosamples": [
{
"name": "<string>",
"ontology": "<string>",
"lifeStage": "<string>",
"sampleType": "<string>",
"displayname": "<string>",
"dnase": "<string|null>",
"h3k4me3": "<string|null>",
"h3k27ac": "<string|null>",
"ctcf": "<string|null>",
"is_type_a": "<bool>"
}
]
}
}
}
```
## 5. `orthologs` (`orthologQuery`)
```json
{
"data": {
"orthologQuery": [
{
"assembly": "<string>", "accession": "<string>",
"ortholog": [
{
"stop": "<int>",
"start": "<int>",
"chromosome": "<string>",
"accession": "<string>"
}
]
}
]
}
}
```
## 6. `linked-genes` (`linkedGenesQuery`)
```json
{
"data": {
"linkedGenesQuery": [
{
"accession": "<string>", "gene": "<string>", "geneid": "<string>",
"genetype": "<string>", "method": "<string>", "effectsize": "<float>",
"assay": "<string>", "celltype": "<string>", "tissue": "<string>",
"score": "<float>", "displayname": "<string>"
}
]
}
}
```
## 7. `entex` (`entexQuery` or `entexActiveAnnotationsQuery`)
For `--accession`:
```json
{
"data": {
"entexQuery": [
{
"assay": "<string>", "accession": "<string>", "hap1_count": "<int>",
"hap2_count": "<int>", "hap1_allele_ratio": "<float>",
"p_betabinom": "<float>", "tissue": "<string>", "donor": "<string>",
"imbalance_significance": "<string>"
}
]
}
}
```
For `--region`:
```json
{
"data": {
"entexActiveAnnotationsQuery": [
{ "tissue": "<string>", "assay_score": "<float>" }
]
}
}
```
## 8. `gene-expression` (combined from `gene`, `gene_quantification`, `gene_dataset`)
The output is post-processed into a flat structure combining gene metadata with
per-experiment TPM values and biosample context.
```json
{
"data": {
"gene": {
"name": "<string>",
"id": "<string>",
"coordinates": {
"start": "<int>", "chromosome": "<string>", "end": "<int>"
}
},
"gene_id_prefix": "<string>",
"assembly": "<string>",
"expression": [
{
"biosample": "<string|null>",
"tissue": "<string|null>",
"cell_compartment": "<string|null>",
"biosample_type": "<string|null>",
"assay_term_name": "<string|null>",
"experiment_accession": "<string>",
"file_accession": "<string>",
"tpm": "<float>",
"fpkm": "<float>"
}
]
}
}
```
## 9. `gwas`
(`getAllGwasStudies`, `getSNPsforGWASStudies`, `getGWASCtEnrichmentQuery`)
```json
{
"data": {
"getAllGwasStudies": [
{
"study": "<string>", "totalldblocks": "<int>", "author": "<string>",
"pubmedid": "<string>", "studyname": "<string>"
}
]
}
}
// OR for snps: "getSNPsforGWASStudies": [...]
// OR for enrichment: "getGWASCtEnrichmentQuery": [...]
```
scripts/encode_portal_api.py
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""A tool to query the ENCODE Portal REST API."""
# /// script
# requires-python = ">=3.10"
# dependencies = [
# "polite-http",
# ]
# ///
import argparse
import json
from polite_http import http_client
BASE_URL = "https://www.encodeproject.org"
_CLIENT = http_client.HttpClient(BASE_URL, qps=10)
def cmd_search(args):
"""Searches the ENCODE Portal API and saves the results.
Args:
args: An argparse namespace containing:
* query: The search query string.
* output: Optional path to save the JSON output.
"""
query = args.query
if not query.startswith("?"):
query = "?" + query
if "format=json" not in query:
query += "&format=json"
url = f"{BASE_URL}/search/{query}"
print(f"[*] Querying: {url}")
data = _CLIENT.fetch_json(url)
print(f"[*] Found {data.get('total', 'unknown')} results.")
# Save output
output_path = args.output or "encode_search_output.json"
with open(output_path, "w") as f:
json.dump(data, f, indent=2)
print(f"[*] Results saved to {output_path}")
def main():
parser = argparse.ArgumentParser(description="Query ENCODE Portal API.")
sub = parser.add_subparsers(dest="command", required=True)
p = sub.add_parser("search", help="Search ENCODE Portal.")
p.add_argument(
"query", help="Query string (e.g., 'type=Experiment&target.label=ZNF549')"
)
p.add_argument("--output", help="Output file path")
p.set_defaults(func=cmd_search)
args = parser.parse_args()
args.func(args)
if __name__ == "__main__":
main()
scripts/screen_api.py
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""A command-line tool to query the ENCODE SCREEN GraphQL API.
This script provides subcommands to interact with the SCREEN API, allowing users
to search for cCREs, get details, find nearby/linked genes, query biosamples,
orthologs, ENTEx data, and GWAS information.
"""
# /// script
# requires-python = ">=3.10"
# dependencies = [
# "polite-http",
# ]
# ///
import argparse
import json
import sys
from polite_http import http_client
API_URL = "https://factorbook.api.wenglab.org/graphql"
_CLIENT = http_client.HttpClient(
API_URL,
qps=10,
default_headers={
"Origin": "https://screen-v2.wenglab.org",
"Referer": "https://screen-v2.wenglab.org/",
},
)
def run_query(query, variables, assembly=None, requires_assembly=True):
"""Execute a GraphQL query with automatic assembly fallback.
Tries the specified assembly first. If none is given and
requires_assembly is True, falls back through grch38 then
mm10 until a non-empty result is returned.
Args:
query: GraphQL query string.
variables: Dict of query variables (modified in-place with the assembly
key).
assembly: Explicit assembly to use, or None for automatic fallback.
requires_assembly: Whether the query needs an assembly variable. Set to
False for assembly-free queries.
Returns:
Tuple of (response_dict, assembly_used, None) on
success, or (None, None, error_string) on failure.
"""
assemblies_to_try = (
[assembly]
if assembly
else (["grch38", "mm10"] if requires_assembly else [None])
)
last_err = None
for asm in assemblies_to_try:
if asm:
variables["assembly"] = asm
try:
result = _CLIENT.fetch_json(
API_URL,
method="POST",
json_body={"query": query, "variables": variables},
)
except (http_client.HttpError, json.JSONDecodeError) as e:
last_err = str(e)
continue
if "errors" in result:
last_err = str(result["errors"])
continue
data = result.get("data", {})
# Check if data is entirely empty or null
if any(data.values()):
return result, asm, None
else:
last_err = "Empty results returned."
return None, None, last_err
def write_output(res, asm, output_path):
"""Write the full API response to the specified JSON file.
Args:
res: Parsed JSON response dict from the API.
asm: Assembly string that was used, or None.
output_path: File path to write the JSON output to.
"""
try:
with open(output_path, "w", encoding="utf-8") as f:
json.dump(res, f, indent=2)
except (OSError, TypeError) as exc:
print(f"Error: Failed to write {output_path}: {exc}", file=sys.stderr)
sys.exit(1)
print(f"Success. Data written to: {output_path}")
if asm:
print(f"Assembly used: {asm}")
def cmd_search(args):
"""Search cCREs by genomic coordinates, accessions, or signal scores."""
query = """
query Search(
$assembly: String!,
$coords: [GenomicRangeInput!],
$accessions: [String!],
$ctcf_start: Float,
$ctcf_end: Float,
$cellType: String
) {
cCRESCREENSearch(
assembly: $assembly,
coordinates: $coords,
accessions: $accessions,
rank_ctcf_start: $ctcf_start,
rank_ctcf_end: $ctcf_end,
cellType: $cellType
) {
chrom start len pct
ctcf_zscore dnase_zscore atac_zscore
enhancer_zscore promoter_zscore
info { accession }
ctspecific {
ct ctcf_zscore dnase_zscore h3k4me3_zscore h3k27ac_zscore
atac_zscore
}
}
}
"""
coords = None
if args.chromosome and args.start and args.end:
coords = [
{"chromosome": args.chromosome, "start": args.start, "end": args.end}
]
variables = {}
if coords:
variables["coords"] = coords
if args.accessions:
variables["accessions"] = args.accessions
if args.ctcf_start is not None:
variables["ctcf_start"] = args.ctcf_start
if args.ctcf_end is not None:
variables["ctcf_end"] = args.ctcf_end
if args.cellType:
variables["cellType"] = args.cellType
res, asm, err = run_query(query, variables, args.assembly)
if err:
print(f"Error: {err}", file=sys.stderr)
sys.exit(1)
write_output(res, asm, args.output)
def cmd_nearby(args):
"""Retrieve nearest genes for one or more cCRE accessions."""
query = """
query Nearby($assembly: String!, $accessions: [String!]) {
cCRESCREENSearch(assembly: $assembly, accessions: $accessions) {
chrom start len pct
info { accession }
nearestgenes { gene distance }
}
}
"""
variables = {"accessions": args.accessions}
res, asm, err = run_query(query, variables, args.assembly)
if err:
print(f"Error: {err}", file=sys.stderr)
sys.exit(1)
write_output(res, asm, args.output)
def cmd_biosamples(args):
"""List available biosample metadata for an assembly."""
query = """
query Biosamples($assembly: String!) {
ccREBiosampleQuery(assembly: $assembly) {
biosamples {
name ontology lifeStage sampleType displayname
dnase: experimentAccession(assay: "DNase")
h3k4me3: experimentAccession(assay: "H3K4me3")
h3k27ac: experimentAccession(assay: "H3K27ac")
ctcf: experimentAccession(assay: "CTCF")
}
}
}
"""
res, asm, err = run_query(query, {}, args.assembly)
if err:
print(f"Error: {err}", file=sys.stderr)
sys.exit(1)
# Enrich results with 'is_type_a' flag (all 4 core assays present)
if res and "data" in res and "ccREBiosampleQuery" in res["data"]:
for b in res["data"]["ccREBiosampleQuery"]["biosamples"]:
b["is_type_a"] = all(
[b.get("dnase"), b.get("h3k4me3"), b.get("h3k27ac"), b.get("ctcf")]
)
write_output(res, asm, args.output)
def cmd_details(args):
"""Get detailed info and biosample z-scores for a single cCRE."""
query = """
query Details($assembly: String!, $accession: [String!]!) {
cCREQuery(assembly: $assembly, accession: $accession) {
accession group
dnase: maxZ(assay: "DNase")
h3k4me3: maxZ(assay: "H3K4me3")
h3k27ac: maxZ(assay: "H3K27ac")
ctcf: maxZ(assay: "CTCF")
atac: maxZ(assay: "ATAC")
coordinates { chromosome start end }
}
ccREBiosampleQuery(assembly: $assembly) {
biosamples {
sampleType name ontology displayname
dnase_acc: experimentAccession(assay: "DNase")
h3k4me3_acc: experimentAccession(assay: "H3K4me3")
h3k27ac_acc: experimentAccession(assay: "H3K27ac")
ctcf_acc: experimentAccession(assay: "CTCF")
cCREZScores(accession: $accession) {
score assay experiment_accession
}
}
}
}
"""
res, asm, err = run_query(
query, {"accession": [args.accession]}, args.assembly
)
if err:
print(f"Error: {err}", file=sys.stderr)
sys.exit(1)
# Enrich results with 'is_type_a' flag (all 4 core assays present)
if res and "data" in res and "ccREBiosampleQuery" in res["data"]:
for b in res["data"]["ccREBiosampleQuery"]["biosamples"]:
b["is_type_a"] = all([
b.get("dnase_acc"),
b.get("h3k4me3_acc"),
b.get("h3k27ac_acc"),
b.get("ctcf_acc"),
])
write_output(res, asm, args.output)
def cmd_orthologs(args):
"""Find orthologous cCREs in another genome assembly."""
query = """
query Orthologs($assembly: String!, $accession: String!) {
orthologQuery(assembly: $assembly, accession: $accession) {
assembly accession
ortholog { stop start chromosome accession }
}
}
"""
res, asm, err = run_query(query, {"accession": args.accession}, args.assembly)
if err:
print(f"Error: {err}", file=sys.stderr)
sys.exit(1)
write_output(res, asm, args.output)
def cmd_linked(args):
"""Retrieve genes linked to one or more cCREs via eQTLs or other methods."""
query = """
query Linked($assembly: String!, $accessions: [String]!) {
linkedGenesQuery(assembly: $assembly, accession: $accessions) {
accession gene geneid genetype method effectsize assay celltype
tissue score displayname
}
}
"""
res, asm, err = run_query(
query, {"accessions": args.accessions}, args.assembly
)
if err:
print(f"Error: {err}", file=sys.stderr)
sys.exit(1)
write_output(res, asm, args.output)
def cmd_entex(args):
"""Query ENTEx allelic imbalance data by accession or region."""
if args.region:
chrom, start, end = args.region.split(":")
start, end = int(start), int(end)
query = """
query EntexRegion($coords: GenomicRangeInput!) {
entexActiveAnnotationsQuery(coordinates: $coords) {
tissue assay_score
}
}
"""
variables = {"coords": {"chromosome": chrom, "start": start, "end": end}}
res, asm, err = run_query(query, variables, requires_assembly=False)
else:
query = """
query Entex($accession: String!) {
entexQuery(accession: $accession) {
assay accession hap1_count hap2_count hap1_allele_ratio
p_betabinom tissue donor imbalance_significance
}
}
"""
variables = {"accession": args.accession}
res, asm, err = run_query(query, variables, requires_assembly=False)
if err:
print(f"Error: {err}", file=sys.stderr)
sys.exit(1)
write_output(res, asm, args.output)
def cmd_gene_expression(args):
"""Retrieve gene expression (TPM and FPKM) across biosamples for a named gene.
TPM (transcripts per million) is the preferred quantification metric;
FPKM (fragments per kilobase per million) is also included for
completeness.
This is a three-step process using the factorbook GraphQL API:
1. Resolve the gene name to an Ensembl gene ID via `gene`.
2. Fetch per-experiment TPM and FPKM data via `gene_quantification`.
3. Enrich with biosample/tissue metadata via `gene_dataset`.
Args:
args: An argparse.Namespace object with the following attributes:
gene: The gene symbol (e.g., OR51B4).
assembly: The genome assembly (e.g., grch38).
output: The path to the output JSON file.
"""
# Step 1: Resolve gene name -> Ensembl ID
gene_query = """
query GeneID($assembly: String!, $name: [String!]) {
gene(assembly: $assembly, name: $name) {
name
id
coordinates {
start chromosome end
}
}
}
"""
res, asm, err = run_query(gene_query, {"name": [args.gene]}, args.assembly)
if err:
print(f"Error resolving gene name: {err}", file=sys.stderr)
sys.exit(1)
genes = res.get("data", {}).get("gene", [])
if not genes:
print(
f"Error: Gene '{args.gene}' not found in assembly.",
file=sys.stderr,
)
sys.exit(1)
gene_info = genes[0]
if len(genes) > 1:
other_ids = ", ".join(g["id"] for g in genes[1:])
print(
f"Warning: Multiple genes matched '{args.gene}'. "
f"Using {gene_info['id']}, ignoring: {other_ids}",
file=sys.stderr,
)
gene_id = gene_info["id"]
# Use the stable ID prefix without version (e.g. ENSG00000183251)
gene_id_prefix = gene_id.split(".")[0]
print(f"Resolved gene '{args.gene}' to ID: {gene_id}")
# Step 2: Fetch per-experiment TPM via gene_quantification
quant_query = """
query GeneQuant($assembly: String!, $gene_id_prefix: [String]) {
gene_quantification(
assembly: $assembly,
gene_id_prefix: $gene_id_prefix,
sortByTpm: true
) {
experiment_accession
file_accession
tpm
fpkm
}
}
"""
# Pin to the assembly that resolved the gene in Step 1. The Ensembl ID is
# assembly-specific, so falling back to a different assembly would be wrong.
res2, _, err2 = run_query(
quant_query,
{"gene_id_prefix": [gene_id_prefix]},
assembly=asm,
)
if err2:
print(f"Error fetching expression data: {err2}", file=sys.stderr)
sys.exit(1)
quants = res2.get("data", {}).get("gene_quantification", [])
if not quants:
print(f"No expression data found for gene '{args.gene}'.", file=sys.stderr)
sys.exit(1)
# Step 3: Enrich with biosample metadata via gene_dataset.
# Chunk accessions to avoid 413 Payload Too Large on the GraphQL endpoint.
exp_accessions = list({q["experiment_accession"] for q in quants})
ds_query = """
query DatasetMeta($accessions: [String]) {
gene_dataset(accession: $accessions) {
accession
biosample
tissue
biosample_type
cell_compartment
assay_term_name
}
}
"""
ds_lookup = {}
chunk_size = 100
for i in range(0, len(exp_accessions), chunk_size):
chunk = exp_accessions[i : i + chunk_size]
res3, _, err3 = run_query(
ds_query,
{"accessions": chunk},
requires_assembly=False,
)
if not err3:
for ds in res3.get("data", {}).get("gene_dataset", []):
ds_lookup[ds["accession"]] = ds
# Merge quantification + dataset metadata
output = {
"data": {
"gene": gene_info,
"gene_id_prefix": gene_id_prefix,
"assembly": asm,
"expression": [],
}
}
for q in quants:
ds = ds_lookup.get(q["experiment_accession"], {})
output["data"]["expression"].append({
"biosample": ds.get("biosample"),
"tissue": ds.get("tissue"),
"cell_compartment": ds.get("cell_compartment"),
"biosample_type": ds.get("biosample_type"),
"assay_term_name": ds.get("assay_term_name"),
"experiment_accession": q["experiment_accession"],
"file_accession": q["file_accession"],
"tpm": q["tpm"],
"fpkm": q["fpkm"],
})
print(
f"Found expression data in {len(output['data']['expression'])} "
"biosample entries."
)
write_output(output, asm, args.output)
def cmd_gwas(args):
"""Query GWAS studies, their SNPs, or cell-type enrichment."""
if args.type == "studies":
query = """
query GWASStudies {
getAllGwasStudies {
study totalldblocks author pubmedid studyname
}
}
"""
res, asm, err = run_query(query, {}, requires_assembly=False)
elif args.type == "snps":
query = """
query GWASSNPs($study: [String!]!) {
getSNPsforGWASStudies(study: $study) {
snpid ldblock rsquare chromosome stop start
}
}
"""
res, asm, err = run_query(
query, {"study": [args.study]}, requires_assembly=False
)
else:
query = """
query GWASEnrichment($study: String!) {
getGWASCtEnrichmentQuery(study: $study) {
celltype accession fc fdr pvalue
}
}
"""
res, asm, err = run_query(
query, {"study": args.study}, requires_assembly=False
)
if err:
print(f"Error: {err}", file=sys.stderr)
sys.exit(1)
write_output(res, asm, args.output)
def main():
"""Parse CLI arguments and dispatch to the appropriate subcommand."""
parser = argparse.ArgumentParser(
description="Query the ENCODE Registry of cCREs via SCREEN GraphQL API."
)
subparsers = parser.add_subparsers(dest="command", required=True)
def add_common(p):
"""Add --assembly and --output arguments shared by most subcommands."""
p.add_argument(
"--assembly",
help=(
"Assembly (e.g., grch38, mm10). Default fallback "
"tries grch38 then mm10."
),
)
p.add_argument(
"--output",
default="/tmp/encode_output.json",
help="Output JSON file path (default: /tmp/encode_output.json).",
)
p_search = subparsers.add_parser(
"search", help="Search cCREs by coordinates, accessions, or signals."
)
add_common(p_search)
p_search.add_argument("--chromosome", help="e.g., chr11")
p_search.add_argument("--start", type=int, help="Start coordinate")
p_search.add_argument("--end", type=int, help="End coordinate")
p_search.add_argument(
"--accessions", nargs="+", help="One or more accessions"
)
p_search.add_argument(
"--ctcf-start", type=float, help="CTCF max z-score range start"
)
p_search.add_argument(
"--ctcf-end", type=float, help="CTCF max z-score range end"
)
p_search.add_argument(
"--cellType",
help="Biosample-specific epigenetic signal (e.g., GM12878_ENCDO000AAK)",
)
p_search.set_defaults(func=cmd_search)
p_nearby = subparsers.add_parser(
"nearby-genes", help="Get nearby genes for cCRE accessions."
)
add_common(p_nearby)
p_nearby.add_argument("accessions", nargs="+", help="One or more accessions")
p_nearby.set_defaults(func=cmd_nearby)
p_biosamples = subparsers.add_parser(
"biosamples", help="Get biosample metadata for an assembly."
)
add_common(p_biosamples)
p_biosamples.set_defaults(func=cmd_biosamples)
p_details = subparsers.add_parser(
"details", help="Get cCRE details and biosample-specific signals."
)
add_common(p_details)
p_details.add_argument(
"accession", help="cCRE accession (e.g., EH38E2941922)"
)
p_details.set_defaults(func=cmd_details)
p_orthologs = subparsers.add_parser(
"orthologs", help="Get orthologous cCREs in another assembly."
)
add_common(p_orthologs)
p_orthologs.add_argument("accession", help="cCRE accession")
p_orthologs.set_defaults(func=cmd_orthologs)
p_linked = subparsers.add_parser(
"linked-genes", help="Get linked genes for a cCRE."
)
add_common(p_linked)
p_linked.add_argument(
"accessions", nargs="+", help="One or more cCRE accessions"
)
p_linked.set_defaults(func=cmd_linked)
p_entex = subparsers.add_parser(
"entex", help="Get ENTEx data for a cCRE or genomic region."
)
g = p_entex.add_mutually_exclusive_group(required=True)
g.add_argument("--accession", help="cCRE accession")
g.add_argument("--region", help="Genomic region format chr:start:end")
p_entex.add_argument(
"--output",
default="/tmp/encode_output.json",
help="Output JSON file path (default: /tmp/encode_output.json).",
)
p_entex.set_defaults(func=cmd_entex)
p_gene_expr = subparsers.add_parser(
"gene-expression",
help="Get gene expression (TPM and FPKM) across biosamples for a gene.",
)
add_common(p_gene_expr)
p_gene_expr.add_argument(
"gene", help="Gene symbol (e.g., OR51B4, GAPDH, TP53)"
)
p_gene_expr.set_defaults(func=cmd_gene_expression)
p_gwas = subparsers.add_parser(
"gwas", help="Query GWAS studies, SNPs, or cell-type enrichment."
)
p_gwas.add_argument(
"type",
choices=["studies", "snps", "enrichment"],
help="Type of GWAS query",
)
p_gwas.add_argument(
"--study", help="Study name (required for snps and enrichment)"
)
p_gwas.add_argument(
"--output",
default="/tmp/encode_output.json",
help="Output JSON file path (default: /tmp/encode_output.json).",
)
p_gwas.set_defaults(func=cmd_gwas)
args = parser.parse_args()
if (
args.command == "gwas"
and args.type in ["snps", "enrichment"]
and not args.study
):
parser.error("--study is required for GWAS snps and enrichment queries.")
args.func(args)
if __name__ == "__main__":
main()
SKILL.md
---
name: encode-ccres-database
description: >
Query the ENCODE Registry of cis-Regulatory Elements (cCREs) via the SCREEN
GraphQL API, or make custom queries to the ENCODE Portal REST API for
experiments and files (ChIP-seq peaks, etc.). Use when you want to query
regulatory annotations or raw experimental data across human cell types.
---
# ENCODE Database Skill
This skill allows you to query the ENCODE Registry of cCREs (candidate
cis-Regulatory Elements) via the SCREEN GraphQL API. It helps identify
functional non-coding DNA elements (like Promoters, Enhancers, and insulators)
by analyzing biochemical signatures (DNase, H3K4me3, H3K27ac, CTCF).
## Prerequisites
1. **`uv`**: Read the `uv` skill and follow its Setup instructions to ensure
`uv` is installed and on PATH.
2. **User Notification**: If .licenses/encode_ccres_database_LICENSE.txt does
not already exist in the workspace root directory then (1) prominently
notify the user to check the terms at
https://www.encodeproject.org/help/rest-api/, then (2) create the file
recording the notification text and timestamp.
## Core Rules
- **Use the Wrapper**: ALWAYS execute the provided helper scripts to query the
database rather than accessing the database directly. The scripts
automatically enforce the required rate limit gracefully.
- **Parsing Output**: Do NOT use `cat` to read the entire JSON output file
into context, as it can be extremely large. You MUST use `jq` to efficiently
parse and extract relevant fields.
- **Notification**: If this skill is used, ensure this is mentioned in the
output.
### Quick Start
```bash
# Search cCREs by coordinates
uv run scripts/screen_api.py search --chromosome chr11 \
--start 5205263 --end 5207263 \
--output /tmp/search.json
# Get details for a specific cCRE
uv run scripts/screen_api.py details EH38E2941922 \
--output /tmp/details.json
```
All subcommands write JSON to disk. Always save output in a temporary location
like `/tmp/`.
### Identifying High-Confidence ("Type A") Biosamples
Biosamples in ENCODE are often categorized by their data completeness. **"Type
A"** (or high-confidence) biosamples are those that have experimental data for
all four core epigenetic markers: **DNase, H3K4me3, H3K27ac, and CTCF**.
The `biosamples` and `details` commands automatically enrich their output with
an `is_type_a` boolean flag for each biosample.
**Example: Finding high-confidence cell types**
```bash
uv run scripts/screen_api.py biosamples --output /tmp/biosamples.json
# Use jq to filter for Type A biosamples
jq '.data.ccREBiosampleQuery.biosamples[] | select(.is_type_a == true) | .displayname' /tmp/biosamples.json
```
### Parsing Output (CRITICAL)
**Do NOT use `cat` to read the entire JSON output file into context, as it**
**can be extremely large.** Instead, you MUST use `jq` to efficiently parse and
extract the relevant fields from the JSON file saved by the script. If `jq` is
not available on the system, write your own Python filtering code (e.g.,
`python3 -c "import json..."`) to extract the necessary data.
For a complete reference of the JSON structure returned by eachmcommand (so you
know which fields to query with `jq`), read
`references/json_output_structure.md`.
### Available Commands
- `search`: Search cCREs by coordinates, accessions, or epigenetic signals.
```bash
uv run scripts/screen_api.py search \
--chromosome chr11 --start 5205263 --end 5207263 \
--output /tmp/search.json
```
- `nearby-genes`: Find nearby genes for given cCRE accessions.
```bash
uv run scripts/screen_api.py nearby-genes \
EH38E1516972 --output /tmp/nearby.json
```
- `details`: Get detailed information and biosample-specific max Z-scores for
a specific cCRE.
```bash
uv run scripts/screen_api.py details EH38E2941922 \
--output /tmp/details.json
```
- `biosamples`: Get biosample metadata for an assembly.
```bash
uv run scripts/screen_api.py biosamples \
--output /tmp/biosamples.json
```
- `orthologs`: Get orthologous cCREs in another assembly.
```bash
uv run scripts/screen_api.py orthologs EH38E2941922 \
--output /tmp/orthologs.json
```
- `linked-genes`: Find linked genes via methods like HiC or eQTLs.
```bash
uv run scripts/screen_api.py linked-genes \
EH38E1516972 --output /tmp/linked.json
```
- `gene-expression`: Get gene expression (TPM) across all biosamples for a
named gene. Internally resolves the gene symbol to an Ensembl gene ID, then
queries per-biosample RNA-seq quantifications.
```bash
uv run scripts/screen_api.py gene-expression GAPDH \
--output /tmp/gene_expr.json
```
- `entex`: Get ENTEx data for a cCRE or genomic region.
```bash
uv run scripts/screen_api.py entex \
--accession EH38E1310345 \
--output /tmp/entex.json
```
```bash
uv run scripts/screen_api.py entex \
--region chr1:1000068:1000409 \
--output /tmp/entex.json
```
- `gwas`: Query genome-wide association studies, SNPs, or enrichment data.
```bash
uv run scripts/screen_api.py gwas studies \
--output /tmp/gwas.json
```
```bash
uv run scripts/screen_api.py gwas snps --study \
Ahola-Olli_AV-27989323-Eotaxin_levels \
--output /tmp/gwas_snps.json
```
You can supply the `--assembly mm10` or `--assembly grch38` flag to explicitly
request a specific assembly for most commands. By default, the script targets
`grch38` but will automatically fall back to `mm10` if no results are found or
if the query fails.
## ENCODE Portal REST API (Direct Access)
For accessing raw experiments, ChIP-seq peaks, or other datasets that are not
represented as cCREs in SCREEN, use the `scripts/encode_portal_api.py` script.
It allows custom queries to the ENCODE Portal REST API.
### Usage
```bash
uv run scripts/encode_portal_api.py search "type=Experiment&target.label=ZNF549" --output /tmp/znf549_experiments.json
```
### Data Analysis Tips
When analyzing `.bed` or `.bigBed` files downloaded from ENCODE, standard
bioinformatics tools are highly recommended for finding overlaps (e.g., between
gene promoters and peaks):
- **`bedtools`**: For fast mathematical operations on genomic intervals.
- **`bigBedToBed`**: For converting binary BigBed files to readable BED
format.
- **`pybedtools`**: A Python wrapper for `bedtools`.
Write custom logic if these tools are not pre-installed.
## Custom Queries (SCREEN GraphQL)
If you need to make a complex GraphQL query that the script does not support,
read `references/graphql_schema.md` for a reference of available queries,
arguments, and return fields in the SCREEN GraphQL API.