Skill Tracer
Skills › Scientific Agent Skills › imaging-data-commons › dicomweb_guide.md

dicomweb_guide.md

426 lines · skills/imaging-data-commons/references/dicomweb_guide.md at 5f2bc03

Open on GitHub
1# DICOMweb Guide for IDC
2
3Reviewed 2026-09-30 against IDC endpoint/policy documentation and Google Healthcare
4conformance. Public QIDO and metadata reads were probed; authenticated Google-store
5access and pixel/frame retrieval were not executed.
6

DICOMweb Guide for IDC

Reviewed 2026-09-30 against IDC endpoint/policy documentation and Google Healthcare conformance. Public QIDO and metadata reads were probed; authenticated Google-store access and pixel/frame retrieval were not executed.

Raw lines 1 to 6
7## When to Use DICOMweb
8
9Use DICOMweb when you need:
10- Integration with PACS systems or DICOMweb-compatible tools
11- Streaming metadata without downloading full files
12- Building custom viewers or web applications
13- Using existing DICOMweb client libraries (OHIF, dicomweb-client, etc.)
15For most use cases, `idc-index` is simpler and recommended. Use DICOMweb when you specifically need the DICOMweb protocol.

When to Use DICOMweb

Use DICOMweb when you need:

  • Integration with PACS systems or DICOMweb-compatible tools
  • Streaming metadata without downloading full files
  • Building custom viewers or web applications
  • Using existing DICOMweb client libraries (OHIF, dicomweb-client, etc.)

For most use cases, idc-index is simpler and recommended. Use DICOMweb when you specifically need the DICOMweb protocol.

Raw lines 7 to 16
17## Endpoints
19### Public Proxy (No Authentication)
21```
22https://proxy.imaging.datacommons.cancer.gov/current/viewer-only-no-downloads-see-tinyurl-dot-com-slash-3j3d9jyp/dicomWeb
23```
25- **100% data coverage** - Contains all IDC data from all storage buckets
26- Points to the latest IDC version automatically
27- **Updates immediately** on new IDC releases
28- Per-IP and global daily egress quotas; use when bucket downloads cannot meet the task
29- No authentication required
30- Read-only access
31- Note: "viewer-only-no-downloads" in URL is legacy naming with no functional meaning

Public Proxy (No Authentication)

https://proxy.imaging.datacommons.cancer.gov/current/viewer-only-no-downloads-see-tinyurl-dot-com-slash-3j3d9jyp/dicomWeb
  • 100% data coverage - Contains all IDC data from all storage buckets
  • Points to the latest IDC version automatically
  • Updates immediately on new IDC releases
  • Per-IP and global daily egress quotas; use when bucket downloads cannot meet the task
  • No authentication required
  • Read-only access
  • Note: "viewer-only-no-downloads" in URL is legacy naming with no functional meaning
Raw lines 19 to 32
33### Google Healthcare API (Requires Authentication)
35```
36https://healthcare.googleapis.com/v1/projects/nci-idc-data/locations/us-central1/datasets/idc/dicomStores/idc-store-v{VERSION}/dicomWeb
37```
39The pattern is historical, not proof that a store exists for the latest IDC release.
40IDC currently documents `idc-store-v23`, while Google
41[documents `idc-store` and an access-request form](https://docs.cloud.google.com/healthcare-api/docs/resources/public-datasets/idc).
42Confirm the provisioned store URL and access with those pages/the IDC health monitor; do
43not synthesize `idc-store-v24` solely from this client output:
45```python
46from idc_index import IDCClient
47client = IDCClient()
48print(client.get_idc_version()) # e.g., "v24" for current version
49```
51- Replicates `idc-open-data`; excludes the other two buckets, so check your selected series
52- IDC targets synchronization within 1-2 weeks of a release; this is not guaranteed
53- Requires authentication and provides higher quotas
54- Better performance (no proxy routing)
55- Each release gets a new versioned store
57See [Content Coverage Differences](#content-coverage-differences) and [Authentication](#authentication-for-google-healthcare-api) sections below.

Google Healthcare API (Requires Authentication)

https://healthcare.googleapis.com/v1/projects/nci-idc-data/locations/us-central1/datasets/idc/dicomStores/idc-store-v{VERSION}/dicomWeb

The pattern is historical, not proof that a store exists for the latest IDC release. IDC currently documents idc-store-v23, while Google [documents idc-store and an access-request form](https://docs.cloud.google.com/healthcare-api/docs/resources/public-datasets/idc). Confirm the provisioned store URL and access with those pages/the IDC health monitor; do not synthesize idc-store-v24 solely from this client output:

from idc_index import IDCClient
client = IDCClient()
print(client.get_idc_version())  # e.g., "v24" for current version
  • Replicates idc-open-data; excludes the other two buckets, so check your selected series
  • IDC targets synchronization within 1-2 weeks of a release; this is not guaranteed
  • Requires authentication and provides higher quotas
  • Better performance (no proxy routing)
  • Each release gets a new versioned store

See Content Coverage Differences and Authentication sections below.

Raw lines 33 to 58
59## Content Coverage Differences
61**Important:** The two DICOMweb endpoints have different data coverage. The IDC public proxy contains MORE data than the authenticated Google Healthcare endpoint.

Content Coverage Differences

Important: The two DICOMweb endpoints have different data coverage. The IDC public proxy contains MORE data than the authenticated Google Healthcare endpoint.

Raw lines 59 to 62
63### Coverage Summary
65| Endpoint | Coverage | Missing Data |
66|----------|----------|--------------|
67| **IDC Public Proxy** | 100% | None |
68| **Google Healthcare API** | Only `idc-open-data` | Two other buckets; possibly newer releases |

Coverage Summary

EndpointCoverageMissing Data
IDC Public Proxy100%None
Google Healthcare APIOnly idc-open-dataTwo other buckets; possibly newer releases
Raw lines 63 to 69
70### What's Missing from Google Healthcare?
72The Google Healthcare DICOM store **only replicates data from the `idc-open-data` S3 bucket**. It does not include data from two additional buckets:
74- `idc-open-data-cr`
75- `idc-open-data-two`
77The often-quoted 95.89% coverage is an IDC v21 series-count snapshot. Recalculate the
78share for the selected release; it is not a current guarantee or a fraction by file size.
80See `cloud_storage_guide.md` for details on bucket organization, file structure, and direct access methods.

What's Missing from Google Healthcare?

The Google Healthcare DICOM store **only replicates data from the idc-open-data S3 bucket**. It does not include data from two additional buckets:

  • idc-open-data-cr
  • idc-open-data-two

The often-quoted 95.89% coverage is an IDC v21 series-count snapshot. Recalculate the share for the selected release; it is not a current guarantee or a fraction by file size.

See cloud_storage_guide.md for details on bucket organization, file structure, and direct access methods.

Raw lines 70 to 81
82### Update Timing
84- **IDC Public Proxy**: Updates immediately when new IDC versions are released
85- **Google Healthcare**: Independently maintained; synchronization within 1-2 weeks is a goal.
87Verify the actual store release and query representative selected UIDs before a large job.
89**Warning from IDC documentation:** *"Google-hosted DICOM store may not contain the latest version of IDC data!"* - Check during the weeks following a new release.

Update Timing

  • IDC Public Proxy: Updates immediately when new IDC versions are released
  • Google Healthcare: Independently maintained; synchronization within 1-2 weeks is a goal.

Verify the actual store release and query representative selected UIDs before a large job.

Warning from IDC documentation: "Google-hosted DICOM store may not contain the latest version of IDC data!" - Check during the weeks following a new release.

Raw lines 82 to 90
91### Choosing the Right Endpoint
93**Use IDC Public Proxy when:**
94- You need complete data coverage (100%)
95- You need the absolute latest data immediately after a new version release
96- You don't want to set up GCP authentication
97- Your usage fits within per-IP quotas (can request increases via support@canceridc.dev)
98- You're accessing slide microscopy images frame-by-frame
100**Use Google Healthcare API when:**
101- Your selected series exist in the particular Google store
102- You need higher quotas for heavy usage
103- You want better performance (direct access, no proxy routing)

Choosing the Right Endpoint

Use IDC Public Proxy when:

  • You need complete data coverage (100%)
  • You need the absolute latest data immediately after a new version release
  • You don't want to set up GCP authentication
  • Your usage fits within per-IP quotas (can request increases via support@canceridc.dev)
  • You're accessing slide microscopy images frame-by-frame

Use Google Healthcare API when:

  • Your selected series exist in the particular Google store
  • You need higher quotas for heavy usage
  • You want better performance (direct access, no proxy routing)
Raw lines 91 to 104
105### Checking Your Data Availability
107Before choosing an endpoint, verify whether your data might be in the missing buckets:
109```python
110from idc_index import IDCClient
112client = IDCClient()
114# Check which buckets contain your collection's data
115results = client.sql_query("""
116 SELECT aws_bucket, COUNT(*) as series_count
117 FROM index
118 WHERE collection_id = 'your_collection_id'
119 GROUP BY aws_bucket
120""")
122print(results)
124# Look for bucket values 'idc-open-data-cr' or 'idc-open-data-two'
125# If present, that data won't be available in Google Healthcare endpoint
126```

Checking Your Data Availability

Before choosing an endpoint, verify whether your data might be in the missing buckets:

from idc_index import IDCClient

client = IDCClient()

# Check which buckets contain your collection's data
results = client.sql_query("""
    SELECT aws_bucket, COUNT(*) as series_count
    FROM index
    WHERE collection_id = 'your_collection_id'
    GROUP BY aws_bucket
""")

print(results)

# Look for bucket values 'idc-open-data-cr' or 'idc-open-data-two'
# If present, that data won't be available in Google Healthcare endpoint
Raw lines 105 to 127
128## Implementation Details
130IDC DICOMweb is provided through Google Cloud Healthcare API DICOM stores. The implementation follows DICOM PS3.18 Web Services with specific characteristics documented in the [Google Healthcare DICOM conformance statement](https://docs.cloud.google.com/healthcare-api/docs/dicom).

Implementation Details

IDC DICOMweb is provided through Google Cloud Healthcare API DICOM stores. The implementation follows DICOM PS3.18 Web Services with specific characteristics documented in the Google Healthcare DICOM conformance statement.

Raw lines 128 to 131
132### Supported Operations
134| Service | Description | Supported |
135|---------|-------------|-----------|
136| QIDO-RS | Search for DICOM objects | Yes |
137| WADO-RS | Retrieve DICOM objects and metadata | Yes |
138| STOW-RS | Store DICOM objects | No (IDC is read-only) |
140**Not supported:** URI Service, Worklist Service, Non-Patient Instance Service, Capabilities Transactions

Supported Operations

ServiceDescriptionSupported
QIDO-RSSearch for DICOM objectsYes
WADO-RSRetrieve DICOM objects and metadataYes
STOW-RSStore DICOM objectsNo (IDC is read-only)

Not supported: URI Service, Worklist Service, Non-Patient Instance Service, Capabilities Transactions

Raw lines 132 to 141
142### Searchable DICOM Tags (QIDO-RS)
144The implementation supports a limited set of searchable tags:
146| Level | Searchable Tags |
147|-------|-----------------|
148| Study | StudyInstanceUID, PatientName, PatientID, AccessionNumber, ReferringPhysicianName, StudyDate |
149| Series | All study tags + SeriesInstanceUID, Modality |
150| Instance | All series tags + SOPInstanceUID |
152**Important:** Only exact matching is supported, except for:
153- StudyDate: supports range queries
154- PatientName: supports fuzzy matching

Searchable DICOM Tags (QIDO-RS)

The implementation supports a limited set of searchable tags:

LevelSearchable Tags
StudyStudyInstanceUID, PatientName, PatientID, AccessionNumber, ReferringPhysicianName, StudyDate
SeriesAll study tags + SeriesInstanceUID, Modality
InstanceAll series tags + SOPInstanceUID

Important: Only exact matching is supported, except for:

  • StudyDate: supports range queries
  • PatientName: supports fuzzy matching
Raw lines 142 to 155
156### Query Limitations
158- Maximum results: 5,000 for studies/series searches; 50,000 for instances
159- Maximum offset: 1,000,000
160- Sequence tags (VR SQ) larger than approximately 1 MiB are omitted without a BulkDataURI;
161 other bulk data can have a BulkDataURI. Retrieve the full instance for omitted sequences.
162- QIDO defaults to 100 studies/series or 1,000 instances. Use `limit` and `offset` for pages,
163 including a final empty-page check; there is no reliable warning header marking exhaustion.
164 WADO series metadata does not use these QIDO pagination parameters.

Query Limitations

  • Maximum results: 5,000 for studies/series searches; 50,000 for instances
  • Maximum offset: 1,000,000
  • Sequence tags (VR SQ) larger than approximately 1 MiB are omitted without a BulkDataURI; other bulk data can have a BulkDataURI. Retrieve the full instance for omitted sequences.
  • QIDO defaults to 100 studies/series or 1,000 instances. Use limit and offset for pages, including a final empty-page check; there is no reliable warning header marking exhaustion. WADO series metadata does not use these QIDO pagination parameters.
Raw lines 156 to 165
166## Code Examples
168All examples use the public proxy endpoint. For authenticated access to Google Healthcare, see the [authentication section](#authentication-for-google-healthcare-api).

Code Examples

All examples use the public proxy endpoint. For authenticated access to Google Healthcare, see the authentication section.

Raw lines 166 to 169
170### Finding UIDs with idc-index
172Use `idc-index` to discover data, then use DICOMweb for metadata access:
174```python
175from idc_index import IDCClient
177client = IDCClient()
179# Find studies of interest
180results = client.sql_query("""
181 SELECT StudyInstanceUID, SeriesInstanceUID, PatientID, Modality
182 FROM index
183 WHERE collection_id = 'tcga_luad' AND Modality = 'CT'
184 LIMIT 5
185""")
187# Use these UIDs with DICOMweb
188study_uid = results.iloc[0]['StudyInstanceUID']
189series_uid = results.iloc[0]['SeriesInstanceUID']
190print(f"Study: {study_uid}")
191print(f"Series: {series_uid}")
192```

Finding UIDs with idc-index

Use idc-index to discover data, then use DICOMweb for metadata access:

from idc_index import IDCClient

client = IDCClient()

# Find studies of interest
results = client.sql_query("""
    SELECT StudyInstanceUID, SeriesInstanceUID, PatientID, Modality
    FROM index
    WHERE collection_id = 'tcga_luad' AND Modality = 'CT'
    LIMIT 5
""")

# Use these UIDs with DICOMweb
study_uid = results.iloc[0]['StudyInstanceUID']
series_uid = results.iloc[0]['SeriesInstanceUID']
print(f"Study: {study_uid}")
print(f"Series: {series_uid}")
Raw lines 170 to 193
194### QIDO-RS: Search by UID
196```python
197import requests
199base_url = "https://proxy.imaging.datacommons.cancer.gov/current/viewer-only-no-downloads-see-tinyurl-dot-com-slash-3j3d9jyp/dicomWeb"
201# Search for a specific study
202study_uid = "1.3.6.1.4.1.14519.5.2.1.6450.9002.307623500513044641407722230440"
203response = requests.get(
204 f"{base_url}/studies",
205 params={"StudyInstanceUID": study_uid},
206 headers={"Accept": "application/dicom+json"},
207 timeout=60,
209response.raise_for_status()
211if response.status_code == 200:
212 studies = response.json()
213 print(f"Found {len(studies)} study")
214```

QIDO-RS: Search by UID

import requests

base_url = "https://proxy.imaging.datacommons.cancer.gov/current/viewer-only-no-downloads-see-tinyurl-dot-com-slash-3j3d9jyp/dicomWeb"

# Search for a specific study
study_uid = "1.3.6.1.4.1.14519.5.2.1.6450.9002.307623500513044641407722230440"
response = requests.get(
    f"{base_url}/studies",
    params={"StudyInstanceUID": study_uid},
    headers={"Accept": "application/dicom+json"},
    timeout=60,
)
response.raise_for_status()

if response.status_code == 200:
    studies = response.json()
    print(f"Found {len(studies)} study")
Raw lines 194 to 215
216### QIDO-RS: List Series in a Study
218```python
219import requests
221base_url = "https://proxy.imaging.datacommons.cancer.gov/current/viewer-only-no-downloads-see-tinyurl-dot-com-slash-3j3d9jyp/dicomWeb"
222study_uid = "1.3.6.1.4.1.14519.5.2.1.6450.9002.307623500513044641407722230440"
224response = requests.get(
225 f"{base_url}/studies/{study_uid}/series",
226 headers={"Accept": "application/dicom+json"},
227 timeout=60,
229response.raise_for_status()
231if response.status_code == 200:
232 series_list = response.json()
233 for series in series_list:
234 # DICOM tags are returned as hex codes
235 series_uid = series.get("0020000E", {}).get("Value", [None])[0]
236 modality = series.get("00080060", {}).get("Value", [None])[0]
237 description = series.get("0008103E", {}).get("Value", [""])[0]
238 print(f"{modality}: {description}")
239```

QIDO-RS: List Series in a Study

import requests

base_url = "https://proxy.imaging.datacommons.cancer.gov/current/viewer-only-no-downloads-see-tinyurl-dot-com-slash-3j3d9jyp/dicomWeb"
study_uid = "1.3.6.1.4.1.14519.5.2.1.6450.9002.307623500513044641407722230440"

response = requests.get(
    f"{base_url}/studies/{study_uid}/series",
    headers={"Accept": "application/dicom+json"},
    timeout=60,
)
response.raise_for_status()

if response.status_code == 200:
    series_list = response.json()
    for series in series_list:
        # DICOM tags are returned as hex codes
        series_uid = series.get("0020000E", {}).get("Value", [None])[0]
        modality = series.get("00080060", {}).get("Value", [None])[0]
        description = series.get("0008103E", {}).get("Value", [""])[0]
        print(f"{modality}: {description}")
Raw lines 216 to 240
241### QIDO-RS: List Instances in a Series
243```python
244import requests
246base_url = "https://proxy.imaging.datacommons.cancer.gov/current/viewer-only-no-downloads-see-tinyurl-dot-com-slash-3j3d9jyp/dicomWeb"
247study_uid = "1.3.6.1.4.1.14519.5.2.1.6450.9002.307623500513044641407722230440"
248series_uid = "1.3.6.1.4.1.14519.5.2.1.6450.9002.217441095430480124587725641302"
250response = requests.get(
251 f"{base_url}/studies/{study_uid}/series/{series_uid}/instances",
252 params={"limit": 10},
253 headers={"Accept": "application/dicom+json"},
254 timeout=60,
256response.raise_for_status()
258if response.status_code == 200:
259 instances = response.json()
260 print(f"Found {len(instances)} instances")
261 for inst in instances[:3]:
262 sop_uid = inst.get("00080018", {}).get("Value", [None])[0]
263 print(f" SOPInstanceUID: {sop_uid}")
264```

QIDO-RS: List Instances in a Series

import requests

base_url = "https://proxy.imaging.datacommons.cancer.gov/current/viewer-only-no-downloads-see-tinyurl-dot-com-slash-3j3d9jyp/dicomWeb"
study_uid = "1.3.6.1.4.1.14519.5.2.1.6450.9002.307623500513044641407722230440"
series_uid = "1.3.6.1.4.1.14519.5.2.1.6450.9002.217441095430480124587725641302"

response = requests.get(
    f"{base_url}/studies/{study_uid}/series/{series_uid}/instances",
    params={"limit": 10},
    headers={"Accept": "application/dicom+json"},
    timeout=60,
)
response.raise_for_status()

if response.status_code == 200:
    instances = response.json()
    print(f"Found {len(instances)} instances")
    for inst in instances[:3]:
        sop_uid = inst.get("00080018", {}).get("Value", [None])[0]
        print(f"  SOPInstanceUID: {sop_uid}")
Raw lines 241 to 265
266### WADO-RS: Retrieve Series Metadata
268```python
269import requests
271base_url = "https://proxy.imaging.datacommons.cancer.gov/current/viewer-only-no-downloads-see-tinyurl-dot-com-slash-3j3d9jyp/dicomWeb"
272study_uid = "1.3.6.1.4.1.14519.5.2.1.6450.9002.307623500513044641407722230440"
273series_uid = "1.3.6.1.4.1.14519.5.2.1.6450.9002.217441095430480124587725641302"
275response = requests.get(
276 f"{base_url}/studies/{study_uid}/series/{series_uid}/metadata",
277 headers={"Accept": "application/dicom+json"},
278 timeout=60,
280response.raise_for_status()
282if response.status_code == 200:
283 instances = response.json()
284 print(f"Retrieved metadata for {len(instances)} instances")
286 # Extract image dimensions from first instance
287 if instances:
288 inst = instances[0]
289 rows = inst.get("00280010", {}).get("Value", [None])[0]
290 cols = inst.get("00280011", {}).get("Value", [None])[0]
291 print(f"Image dimensions: {rows} x {cols}")
292```

WADO-RS: Retrieve Series Metadata

import requests

base_url = "https://proxy.imaging.datacommons.cancer.gov/current/viewer-only-no-downloads-see-tinyurl-dot-com-slash-3j3d9jyp/dicomWeb"
study_uid = "1.3.6.1.4.1.14519.5.2.1.6450.9002.307623500513044641407722230440"
series_uid = "1.3.6.1.4.1.14519.5.2.1.6450.9002.217441095430480124587725641302"

response = requests.get(
    f"{base_url}/studies/{study_uid}/series/{series_uid}/metadata",
    headers={"Accept": "application/dicom+json"},
    timeout=60,
)
response.raise_for_status()

if response.status_code == 200:
    instances = response.json()
    print(f"Retrieved metadata for {len(instances)} instances")

    # Extract image dimensions from first instance
    if instances:
        inst = instances[0]
        rows = inst.get("00280010", {}).get("Value", [None])[0]
        cols = inst.get("00280011", {}).get("Value", [None])[0]
        print(f"Image dimensions: {rows} x {cols}")
Raw lines 266 to 293
294### Combined Workflow: idc-index Discovery + DICOMweb Metadata
296```python
297from idc_index import IDCClient
298import requests
300# Use idc-index for efficient discovery
301idc = IDCClient()
302results = idc.sql_query("""
303 SELECT StudyInstanceUID, SeriesInstanceUID, Modality, SeriesDescription
304 FROM index
305 WHERE collection_id = 'nlst' AND Modality = 'CT'
306 LIMIT 1
307""")
309study_uid = results.iloc[0]['StudyInstanceUID']
310series_uid = results.iloc[0]['SeriesInstanceUID']
311print(f"Found: {results.iloc[0]['SeriesDescription']}")
313# Use DICOMweb to stream metadata without downloading files
314base_url = "https://proxy.imaging.datacommons.cancer.gov/current/viewer-only-no-downloads-see-tinyurl-dot-com-slash-3j3d9jyp/dicomWeb"
316response = requests.get(
317 f"{base_url}/studies/{study_uid}/series/{series_uid}/metadata",
318 headers={"Accept": "application/dicom+json"},
319 timeout=60,
321response.raise_for_status()
323if response.status_code == 200:
324 metadata = response.json()
325 print(f"Retrieved metadata for {len(metadata)} instances without downloading files")
326```

Combined Workflow: idc-index Discovery + DICOMweb Metadata

from idc_index import IDCClient
import requests

# Use idc-index for efficient discovery
idc = IDCClient()
results = idc.sql_query("""
    SELECT StudyInstanceUID, SeriesInstanceUID, Modality, SeriesDescription
    FROM index
    WHERE collection_id = 'nlst' AND Modality = 'CT'
    LIMIT 1
""")

study_uid = results.iloc[0]['StudyInstanceUID']
series_uid = results.iloc[0]['SeriesInstanceUID']
print(f"Found: {results.iloc[0]['SeriesDescription']}")

# Use DICOMweb to stream metadata without downloading files
base_url = "https://proxy.imaging.datacommons.cancer.gov/current/viewer-only-no-downloads-see-tinyurl-dot-com-slash-3j3d9jyp/dicomWeb"

response = requests.get(
    f"{base_url}/studies/{study_uid}/series/{series_uid}/metadata",
    headers={"Accept": "application/dicom+json"},
    timeout=60,
)
response.raise_for_status()

if response.status_code == 200:
    metadata = response.json()
    print(f"Retrieved metadata for {len(metadata)} instances without downloading files")
Raw lines 294 to 327
328## Common DICOM Tags Reference
330DICOMweb returns tags as hexadecimal codes. Common tags:
332| Tag | Name | Description |
333|-----|------|-------------|
334| 00080018 | SOPInstanceUID | Unique instance identifier |
335| 00080020 | StudyDate | Date study was performed |
336| 00080060 | Modality | Imaging modality (CT, MR, PT, etc.) |
337| 0008103E | SeriesDescription | Description of series |
338| 00100020 | PatientID | Patient identifier |
339| 0020000D | StudyInstanceUID | Unique study identifier |
340| 0020000E | SeriesInstanceUID | Unique series identifier |
341| 00280010 | Rows | Image height in pixels |
342| 00280011 | Columns | Image width in pixels |

Common DICOM Tags Reference

DICOMweb returns tags as hexadecimal codes. Common tags:

TagNameDescription
00080018SOPInstanceUIDUnique instance identifier
00080020StudyDateDate study was performed
00080060ModalityImaging modality (CT, MR, PT, etc.)
0008103ESeriesDescriptionDescription of series
00100020PatientIDPatient identifier
0020000DStudyInstanceUIDUnique study identifier
0020000ESeriesInstanceUIDUnique series identifier
00280010RowsImage height in pixels
00280011ColumnsImage width in pixels
Raw lines 328 to 343
344## Authentication for Google Healthcare API
346To use the Google Healthcare endpoint with higher quotas:
348```python
349import os
350from google.auth import default
351from google.auth.transport.requests import AuthorizedSession
353# Get ADC; AuthorizedSession refreshes expiring tokens automatically.
354credentials, project = default(scopes=["https://www.googleapis.com/auth/cloud-platform"])
355session = AuthorizedSession(credentials)
357# Set IDC_DICOMWEB_URL to the verified provisioned Google store URL (ending /dicomWeb).
358base_url = os.environ["IDC_DICOMWEB_URL"].rstrip("/")
360response = session.get(
361 f"{base_url}/studies",
362 params={"limit": 5},
363 headers={
364 "Accept": "application/dicom+json"
365 },
366 timeout=60,
368response.raise_for_status()
369```
371**Prerequisites:**
3721. Google Cloud SDK installed (`gcloud`)
3732. Authenticated: `gcloud auth application-default login`
3743. `google-auth` and `requests` installed; account authorized for the selected Google store
3754. `IDC_DICOMWEB_URL` set to the verified endpoint. Google documents an access-request form;
376 follow it if ADC alone is insufficient. No authenticated access was verified in this refresh.

Authentication for Google Healthcare API

To use the Google Healthcare endpoint with higher quotas:

import os
from google.auth import default
from google.auth.transport.requests import AuthorizedSession

# Get ADC; AuthorizedSession refreshes expiring tokens automatically.
credentials, project = default(scopes=["https://www.googleapis.com/auth/cloud-platform"])
session = AuthorizedSession(credentials)

# Set IDC_DICOMWEB_URL to the verified provisioned Google store URL (ending /dicomWeb).
base_url = os.environ["IDC_DICOMWEB_URL"].rstrip("/")

response = session.get(
    f"{base_url}/studies",
    params={"limit": 5},
    headers={
        "Accept": "application/dicom+json"
    },
    timeout=60,
)
response.raise_for_status()

Prerequisites:

  1. Google Cloud SDK installed (gcloud)
  2. Authenticated: gcloud auth application-default login
  3. google-auth and requests installed; account authorized for the selected Google store
  4. IDC_DICOMWEB_URL set to the verified endpoint. Google documents an access-request form; follow it if ADC alone is insufficient. No authenticated access was verified in this refresh.
Raw lines 344 to 377
378## Troubleshooting

Troubleshooting

Raw lines 378 to 379
380### Issue: 400 Bad Request on search queries
381- **Cause:** Using unsupported search parameters. The implementation only supports specific DICOM tags for filtering.
382- **Solution:** Use UID-based queries (StudyInstanceUID, SeriesInstanceUID). For filtering by Modality or other attributes, use `idc-index` to discover UIDs first, then query DICOMweb with specific UIDs.

Issue: 400 Bad Request on search queries

  • Cause: Using unsupported search parameters. The implementation only supports specific DICOM tags for filtering.
  • Solution: Use UID-based queries (StudyInstanceUID, SeriesInstanceUID). For filtering by Modality or other attributes, use idc-index to discover UIDs first, then query DICOMweb with specific UIDs.
Raw lines 380 to 383
384### Issue: 403 Forbidden on Google Healthcare endpoint
385- **Cause:** Missing authentication or insufficient permissions
386- **Solution:** Run `gcloud auth application-default login` and ensure your account has access

Issue: 403 Forbidden on Google Healthcare endpoint

  • Cause: Missing authentication or insufficient permissions
  • Solution: Run gcloud auth application-default login and ensure your account has access
Raw lines 384 to 387
388### Issue: 429 Too Many Requests
389- **Cause:** Rate limit exceeded
390- **Solution:** Honor `Retry-After`, back off, and use public buckets for complete files.
391 A daily proxy egress cap is not fixed by repeated retries; do not rotate IPs to bypass it.

Issue: 429 Too Many Requests

  • Cause: Rate limit exceeded
  • Solution: Honor Retry-After, back off, and use public buckets for complete files. A daily proxy egress cap is not fixed by repeated retries; do not rotate IPs to bypass it.
Raw lines 388 to 392
393### Issue: 204 No Content for valid UIDs
394- **Cause:** UID may be from an older IDC version not in current data, or data is in buckets not replicated by Google Healthcare
395- **Solution:**
396 - Verify UID exists using `idc-index` query first
397 - Check if data is in `idc-open-data-cr` or `idc-open-data-two` buckets (not available in Google Healthcare endpoint)
398 - Switch to IDC public proxy for 100% coverage
399 - Verify store release/access; synchronization timing is not guaranteed

Issue: 204 No Content for valid UIDs

  • Cause: UID may be from an older IDC version not in current data, or data is in buckets not replicated by Google Healthcare
  • Solution:
  • Verify UID exists using idc-index query first
  • Check if data is in idc-open-data-cr or idc-open-data-two buckets (not available in Google Healthcare endpoint)
  • Switch to IDC public proxy for 100% coverage
  • Verify store release/access; synchronization timing is not guaranteed
Raw lines 393 to 400
401### Issue: Large metadata responses slow to parse
402- **Cause:** Series with many instances returns large JSON
403- **Solution:** Use `limit`/`offset` on QIDO instance searches, or retrieve metadata for a
404 specific instance by appending `/instances/{SOPInstanceUID}/metadata`; do not assume
405 `limit` paginates WADO series metadata

Issue: Large metadata responses slow to parse

  • Cause: Series with many instances returns large JSON
  • Solution: Use limit/offset on QIDO instance searches, or retrieve metadata for a specific instance by appending /instances/{SOPInstanceUID}/metadata; do not assume limit paginates WADO series metadata
Raw lines 401 to 406
407### Issue: Response missing expected attributes
408- **Cause:** DICOM sequences larger than ~1 MB are excluded from metadata responses
409- **Solution:** Retrieve the full DICOM instance using WADO-RS instance retrieval if you need all attributes

Issue: Response missing expected attributes

  • Cause: DICOM sequences larger than ~1 MB are excluded from metadata responses
  • Solution: Retrieve the full DICOM instance using WADO-RS instance retrieval if you need all attributes
Raw lines 407 to 410
411## Resources
413**IDC Documentation:**
414- [IDC DICOM Stores](https://learn.canceridc.dev/data/organization-of-data/dicom-stores) - Data coverage and bucket details
415- [IDC DICOMweb Access](https://learn.canceridc.dev/data/downloading-data/dicomweb-access) - Endpoint usage and differences
416- [IDC Proxy Policy](https://learn.canceridc.dev/portal/proxy-policy) - Quota policies and usage restrictions
417- [IDC User Guide](https://learn.canceridc.dev/) - Complete documentation
419**DICOMweb Standards and Tools:**
420- [Google Healthcare DICOM Conformance Statement](https://docs.cloud.google.com/healthcare-api/docs/dicom)
421- [DICOMweb Standard](https://www.dicomstandard.org/using/dicomweb)
422- [dicomweb-client Python library](https://dicomweb-client.readthedocs.io/)
424**Related Guides:**
425- `cloud_storage_guide.md` - Direct bucket access, file organization, CRDC UUIDs, and versioning
426- `bigquery_guide.md` - Advanced metadata queries with full DICOM attributes

Resources

IDC Documentation:

DICOMweb Standards and Tools:

Related Guides:

  • cloud_storage_guide.md - Direct bucket access, file organization, CRDC UUIDs, and versioning
  • bigquery_guide.md - Advanced metadata queries with full DICOM attributes
Raw lines 411 to 426