# How to Implement SteamGridDB Artwork Fetching in RomM: A Complete Guide

> Learn to implement SteamGridDB artwork fetching in RomM. This guide details the layered architecture, API key usage, and artwork retrieval during ROM scans for your ROM collection.

- Repository: [The RomM Project/romm](https://github.com/rommapp/romm)
- Tags: how-to-guide
- Published: 2026-07-05

---

**RomM fetches artwork from SteamGridDB through a layered architecture where `SGDBBaseHandler` coordinates with `SteamGridDBService` to query the SGDB API using your `STEAMGRIDDB_API_KEY`, retrieving cover art during ROM scans and storing URLs in the ROM model.**

SteamGridDB provides high-quality game artwork for emulation frontends, and RomM integrates this service directly into its metadata pipeline. Implementing SteamGridDB artwork fetching in RomM requires understanding the handler-service pattern used throughout the codebase. This guide explains the complete implementation flow from environment configuration to scan-time integration, referencing the actual source code from the `rommapp/romm` repository.

## Prerequisites and Configuration

Before fetching artwork, you must configure your API key. According to the RomM source code in [`config.py`](https://github.com/rommapp/romm/blob/main/config.py), the SGDB client activates only when `STEAMGRIDDB_API_KEY` is present in your environment variables. The handler exposes this state through `SGDBBaseHandler.is_enabled()`【sgdb_handler.py†L33-L36】, which returns `True` only when a valid key is detected, preventing unnecessary API calls.

## The Service Layer Architecture

The `SteamGridDBService` class in [`backend/adapters/services/steamgriddb.py`](https://github.com/rommapp/romm/blob/main/backend/adapters/services/steamgriddb.py) encapsulates all HTTP communication with the official SGDB API. It constructs URLs using `yarl.URL` and injects the required `Authorization: Bearer <API-key>` header via aio-http middleware【steamgriddb.py†L29-L42】. The service returns typed data structures defined in [`steamgriddb_types.py`](https://github.com/rommapp/romm/blob/main/steamgriddb_types.py), ensuring type safety across the async boundary when handling paginated grid resources【steamgriddb.py†L49-L85】.

## Metadata Handler Implementation

The `SGDBBaseHandler` in [`backend/handler/metadata/sgdb_handler.py`](https://github.com/rommapp/romm/blob/main/backend/handler/metadata/sgdb_handler.py) extends `MetadataHandler` and provides the primary interface for artwork retrieval. It implements three public async methods that coordinate with the service layer to fetch game data and cover images.

### Health Checks and Validation

The `heartbeat()` method performs a quick connectivity check by calling `get_game_by_id(1)`. This verifies that your API key is valid and the SGDB service is reachable before attempting bulk artwork operations.

### Fetching Covers by ID

To retrieve artwork for a known game, use `get_rom_by_id(sgdb_id)`. This method first fetches the game record, then calls `_get_game_covers()` to collect all grid resources, returning the first valid URL found【sgdb_handler.py†L49-L77】. This is the fastest path when you already know the SGDB identifier.

### Searching Games by Name

When the SGDB ID is unknown, `get_details(search_term)` searches SGDB by name, then fetches cover grids for each candidate using `iter_grids_for_game`. It returns a list of `SGDBResult` objects containing the game name and all matching resources【sgdb_handler.py†L81-L96】, allowing you to select the best match programmatically.

## Cover Retrieval Logic and Image Types

The `_get_game_covers()` method implements sophisticated image selection logic. It builds a preference list for dimensions including `STEAM_VERTICAL`, `GOG_GALAXY_TILE`, and other formats, alongside grid types (`STATIC` and `ANIMATED`). The method iterates through paginated SGDB endpoints via `iter_grids_for_game`, extracting both thumbnail and full-size URLs while normalizing the type to `static` or `animated`【sgdb_handler.py†L47-L62】【sgdb_handler.py†L71-L87】.

```python
from backend.handler.metadata.sgdb_handler import sgdb_handler

async def fetch_cover(sgdb_id: int):
    rom = await sgdb_handler.get_rom_by_id(sgdb_id)
    return rom.get("url_cover")

```

## Integration with the Scan Handler

During ROM scanning, the system invokes the SGDB handler in [`backend/handler/scan_handler.py`](https://github.com/rommapp/romm/blob/main/backend/handler/scan_handler.py). When processing metadata, the scan handler checks if the handler is enabled via `is_enabled()`, then calls `get_rom_by_id(sgdb_id)` to fetch cover URLs【scan_handler.py†L1015-L1030】. The resulting artwork URL is attached directly to the ROM model. Additionally, the ROM endpoint schema in [`backend/endpoints/roms/__init__.py`](https://github.com/rommapp/romm/blob/main/backend/endpoints/roms/__init__.py) includes an optional `sgdb_id` field to persist the identifier for future reuse【endpoints/roms/__init__.py†L135-L138】.

```python

# inside backend/handler/scan_handler.py

if SGDBHandler.is_enabled():
    sgdb_data = await sgdb_handler.get_rom_by_id(sgdb_id)
    if sgdb_data.get("url_cover"):
        rom.cover_url = sgdb_data["url_cover"]

```

## Error Handling and API Key Validation

All SGDB calls are wrapped in try/except blocks to ensure scan stability. HTTP 401 responses trigger a custom `SGDBInvalidAPIKeyException`, while other failures log warnings and return empty results【steamgriddb.py†L64-L71】. This design prevents a single metadata failure from crashing the entire scan process, allowing RomM to fall back to other artwork sources gracefully.

```python
from backend.handler.metadata.sgdb_handler import sgdb_handler

async def best_cover(search_name: str):
    results = await sgdb_handler.get_details(search_name)
    if not results:
        return None
    # pick the first result's first resource

    first = results[0]["resources"][0]
    return first["url"]

```

## Summary

- **Configuration** requires the `STEAMGRIDDB_API_KEY` environment variable to enable the handler via `is_enabled()`
- **Service Layer** uses `SteamGridDBService` to manage HTTP requests with Bearer token authentication and `yarl.URL` construction
- **Handler Methods** include `heartbeat()`, `get_rom_by_id()`, and `get_details()` for health checks and artwork retrieval
- **Cover Logic** prioritizes specific dimensions like `STEAM_VERTICAL` and supports both `static` and `animated` grid types
- **Integration** occurs during ROM scanning via [`scan_handler.py`](https://github.com/rommapp/romm/blob/main/scan_handler.py), with SGDB IDs stored in the ROM schema for reuse
- **Error Handling** uses specific exceptions for invalid keys and graceful degradation for API failures

## Frequently Asked Questions

### What do I need to configure before using SteamGridDB in RomM?

You must set the `STEAMGRIDDB_API_KEY` environment variable in your configuration. The handler automatically checks for this key via `SGDBBaseHandler.is_enabled()` and will only attempt API calls when the key is present, as implemented in [`sgdb_handler.py`](https://github.com/rommapp/romm/blob/main/sgdb_handler.py).

### How does RomM handle different image types from SteamGridDB?

The `_get_game_covers()` method requests both `STATIC` and `ANIMATED` grid types, preferring specific dimensions like `STEAM_VERTICAL` and `GOG_GALAXY_TILE`. It iterates through paginated results and returns the first matching resource URL, normalizing the type to `static` or `animated` for consistency.

### Where does the artwork fetching happen during a ROM scan?

The scan handler in [`backend/handler/scan_handler.py`](https://github.com/rommapp/romm/blob/main/backend/handler/scan_handler.py) invokes `get_rom_by_id()` when metadata is being refreshed, typically around lines 1015-1030. The fetched cover URL is then assigned to the ROM model's cover field, with the `sgdb_id` stored in the database schema for future lookups.

### What happens if my SteamGridDB API key is invalid?

RomM raises `SGDBInvalidAPIKeyException` for HTTP 401 responses and logs warnings for other failures in [`steamgriddb.py`](https://github.com/rommapp/romm/blob/main/steamgriddb.py). The handler returns empty results rather than crashing, allowing the scan to continue with other metadata sources while alerting you to authentication issues.