Assets API
Die Assets API ermöglicht den Zugriff auf Bilder und andere Medien-Dateien mit dynamischen Transformationsparametern. Sie bietet optimiertes Bild-Delivery mit Format-Konvertierung, Größenanpassung und verschiedenen Anpassungsoptionen.
Diese API liefert Assets aus dem Kulturpool-CMS — Logos, Favicons, Hero-Bilder und redaktionelle Medien. Die Digitalisate der Sammlungsobjekte werden nicht hierüber ausgeliefert; deren URLs finden Sie in den Feldern previewImage und isShownBy der Suche API bzw. im EDM-Datensatz der Objekt API.
Basis-URL
https://api.kulturpool.at/assets
Endpunkt
Asset abrufen
GET /assets/{asset_id}
Ruft ein Asset vom CMS ab und wendet die angegebenen Transformationsparameter an.
Parameter
| Parameter | Typ | Beschreibung | Beispiel |
|---|---|---|---|
asset_id | string | Asset-ID (UUID) aus dem CMS (Pfad-Parameter) | 8e1cf6a9-d1d6-4090-9a36-303f1f57a016 |
width | integer | Breite in Pixeln (> 0) | 1500 |
height | integer | Höhe in Pixeln (> 0) | 700 |
quality | integer | Bildqualität (1–100) | 80 |
without_enlargement | boolean | Automatische Vergrößerung deaktivieren | true |
format | string | Ausgabeformat | auto, avif, webp, jpg, png, tiff |
fit | string | Anpassungsmodus | cover, contain, inside, outside |
without_enlargementDer Parameter heißt in der Kulturpool-API without_enlargement (mit Unterstrichen). Intern wird er auf den Directus-Parameter withoutEnlargement abgebildet — beim Aufruf der Kulturpool-API ist aber die Schreibweise mit Unterstrichen zu verwenden. Ältere Dokumentation nannte hier fälschlich withoutEnlargement.
Beispiele
# Einfacher Asset-Abruf
curl "https://api.kulturpool.at/assets/8e1cf6a9-d1d6-4090-9a36-303f1f57a016"
# Optimiertes Thumbnail
curl "https://api.kulturpool.at/assets/8e1cf6a9-d1d6-4090-9a36-303f1f57a016?format=webp&width=300&height=200&fit=cover&quality=85"
# Hero-Image für moderne Browser
curl "https://api.kulturpool.at/assets/8e1cf6a9-d1d6-4090-9a36-303f1f57a016?format=avif&width=1920&height=800&fit=cover&without_enlargement=true"
Parameter im Detail
Format-Optionen
| Format | Beschreibung | Verwendung |
|---|---|---|
auto | Automatische Format-Auswahl (Standard) | Versucht WebP/AVIF für moderne Browser, sonst JPEG |
webp | WebP-Format | Gute Kompression, breite Browser-Unterstützung |
avif | AVIF-Format | Beste Kompression, neuere Browser |
jpg | JPEG-Format | Universelle Kompatibilität |
png | PNG-Format | Für Transparenz und verlustfreie Kompression |
tiff | TIFF-Format | Hochqualitative Archivierung |
Anpassungsmodi (fit)
| Modus | Beschreibung | Verwendung |
|---|---|---|
cover | Bild vollständig in Dimensionen einpassen (Standard) | Hero-Images, Thumbnails |
contain | Bild mit Letterboxing in Dimensionen einpassen | Logos, vollständige Bilder |
inside | So groß wie möglich innerhalb der Dimensionen | Responsive Bilder |
outside | So klein wie möglich innerhalb/außerhalb der Dimensionen | Spezielle Layouts |
Qualitätseinstellungen
| Qualität | Beschreibung | Dateigröße | Verwendung |
|---|---|---|---|
95-100 | Höchste Qualität | Sehr groß | Druckvorbereitung |
85-95 | Hohe Qualität | Groß | Hero-Images |
75-85 | Gute Qualität | Mittel | Standard-Bilder |
60-75 | Moderate Qualität | Klein | Thumbnails |
40-60 | Niedrige Qualität | Sehr klein | Vorschaubilder |
Ohne Angabe von quality gilt der formatabhängige Standardwert des CMS.
Antwort
Die API prüft die Existenz des Assets und gibt anschließend eine HTTP 302 Redirect auf das transformierte Asset im CMS zurück:
HTTP/1.1 302 Found
Location: https://edit.kulturpool.at/assets/8e1cf6a9-d1d6-4090-9a36-303f1f57a016/?format=webp&width=1500&height=700&fit=cover
HTTP-Clients folgen dem Redirect in der Regel automatisch. In <img>-Tags und CSS kann die API-URL daher direkt verwendet werden.
Beispiel-Code
Responsive Bilder in HTML
<picture>
<source
srcset="https://api.kulturpool.at/assets/8e1cf6a9-d1d6-4090-9a36-303f1f57a016?format=avif&width=1920&height=800&fit=cover"
type="image/avif"
media="(min-width: 1200px)">
<source
srcset="https://api.kulturpool.at/assets/8e1cf6a9-d1d6-4090-9a36-303f1f57a016?format=webp&width=1200&height=600&fit=cover"
type="image/webp"
media="(min-width: 768px)">
<source
srcset="https://api.kulturpool.at/assets/8e1cf6a9-d1d6-4090-9a36-303f1f57a016?format=webp&width=800&height=400&fit=cover"
type="image/webp">
<img
src="https://api.kulturpool.at/assets/8e1cf6a9-d1d6-4090-9a36-303f1f57a016?format=jpg&width=800&height=400&fit=cover"
alt="Beschreibung"
loading="lazy">
</picture>
JavaScript Asset-URL Generator
class AssetUrlBuilder {
constructor(baseUrl = 'https://api.kulturpool.at/assets') {
this.baseUrl = baseUrl;
}
build(assetId, options = {}) {
const {
width,
height,
quality = 85,
format = 'auto',
fit = 'cover',
withoutEnlargement = false,
} = options;
const params = new URLSearchParams();
if (width) params.set('width', width);
if (height) params.set('height', height);
if (quality !== 85) params.set('quality', quality);
if (format !== 'auto') params.set('format', format);
if (fit !== 'cover') params.set('fit', fit);
// Achtung: der API-Parameter heißt without_enlargement
if (withoutEnlargement) params.set('without_enlargement', 'true');
const queryString = params.toString();
return `${this.baseUrl}/${assetId}${queryString ? `?${queryString}` : ''}`;
}
// Vordefinierte Größen
thumbnail(assetId, size = 150) {
return this.build(assetId, {
width: size,
height: size,
format: 'webp',
quality: 80,
});
}
hero(assetId, width = 1920, height = 800) {
return this.build(assetId, {
width,
height,
format: 'webp',
quality: 85,
fit: 'cover',
});
}
logo(assetId, maxWidth = 200) {
return this.build(assetId, {
width: maxWidth,
format: 'png',
fit: 'contain',
withoutEnlargement: true,
});
}
}
// Verwendung
const assetBuilder = new AssetUrlBuilder();
const thumbnailUrl = assetBuilder.thumbnail('8e1cf6a9-d1d6-4090-9a36-303f1f57a016');
const heroUrl = assetBuilder.hero('8e1cf6a9-d1d6-4090-9a36-303f1f57a016', 1500, 600);
Python Asset-Manager
from urllib.parse import urlencode
class AssetManager:
def __init__(self, base_url="https://api.kulturpool.at/assets"):
self.base_url = base_url
def build_url(self, asset_id, **kwargs):
"""Asset-URL mit Parametern erstellen"""
params = {k: v for k, v in kwargs.items() if v is not None}
# Boolean-Werte in Strings konvertieren
for key, value in params.items():
if isinstance(value, bool):
params[key] = str(value).lower()
url = f"{self.base_url}/{asset_id}"
if params:
url += f"?{urlencode(params)}"
return url
def get_responsive_urls(self, asset_id, format_type="webp"):
"""Responsive Bild-URLs für verschiedene Bildschirmgrößen"""
return {
"mobile": self.build_url(asset_id, width=480, height=320, format=format_type, fit="cover"),
"tablet": self.build_url(asset_id, width=768, height=512, format=format_type, fit="cover"),
"desktop": self.build_url(asset_id, width=1200, height=800, format=format_type, fit="cover"),
"large": self.build_url(asset_id, width=1920, height=1080, format=format_type, fit="cover"),
}
def get_optimized_thumbnail(self, asset_id, size=300):
"""Optimiertes Thumbnail"""
return self.build_url(
asset_id,
width=size,
height=size,
format="webp",
quality=80,
fit="cover",
)
# Verwendung
asset_manager = AssetManager()
asset_id = "8e1cf6a9-d1d6-4090-9a36-303f1f57a016"
# Responsive URLs
responsive_urls = asset_manager.get_responsive_urls(asset_id)
print(f"Mobile: {responsive_urls['mobile']}")
# Thumbnail
thumbnail_url = asset_manager.get_optimized_thumbnail(asset_id, 150)
print(f"Thumbnail: {thumbnail_url}")
Anwendungsfälle
Integration mit der Institutionen API
// Asset-URLs für Institution Hero-Images generieren
async function getInstitutionWithOptimizedImages(institutionId) {
const response = await fetch(
`https://api.kulturpool.at/institutions/${institutionId}`,
);
const {data: institution} = await response.json();
const assetBuilder = new AssetUrlBuilder();
return {
...institution,
optimizedImages: {
favicon: institution.favicon?.id
? assetBuilder.build(institution.favicon.id, {
width: 32,
height: 32,
format: 'png',
})
: null,
logo: institution.logo?.id
? assetBuilder.logo(institution.logo.id, 200)
: null,
hero: institution.hero_image?.id
? assetBuilder.hero(institution.hero_image.id)
: null,
heroMobile: institution.hero_image?.id
? assetBuilder.build(institution.hero_image.id, {
width: 768,
height: 400,
format: 'webp',
fit: 'cover',
})
: null,
},
};
}
Bildergalerie mit Lazy Loading
class ImageGallery {
constructor(containerId) {
this.container = document.getElementById(containerId);
this.assetBuilder = new AssetUrlBuilder();
this.observer = new IntersectionObserver(this.handleIntersection.bind(this));
}
addImage(assetId, alt = '') {
const imageContainer = document.createElement('div');
imageContainer.className = 'gallery-item';
imageContainer.dataset.assetId = assetId;
imageContainer.dataset.alt = alt;
// Placeholder
const placeholder = document.createElement('div');
placeholder.className = 'image-placeholder';
placeholder.style.backgroundColor = '#f0f0f0';
placeholder.style.aspectRatio = '16/9';
imageContainer.appendChild(placeholder);
this.container.appendChild(imageContainer);
// Für Lazy Loading beobachten
this.observer.observe(imageContainer);
}
handleIntersection(entries) {
entries.forEach((entry) => {
if (entry.isIntersecting) {
this.loadImage(entry.target);
this.observer.unobserve(entry.target);
}
});
}
loadImage(container) {
const assetId = container.dataset.assetId;
const alt = container.dataset.alt;
const picture = document.createElement('picture');
// WebP für moderne Browser
const sourceWebP = document.createElement('source');
sourceWebP.srcset = this.assetBuilder.build(assetId, {
width: 800,
height: 450,
format: 'webp',
fit: 'cover',
});
sourceWebP.type = 'image/webp';
// JPEG Fallback
const img = document.createElement('img');
img.src = this.assetBuilder.build(assetId, {
width: 800,
height: 450,
format: 'jpg',
fit: 'cover',
});
img.alt = alt;
img.loading = 'lazy';
picture.appendChild(sourceWebP);
picture.appendChild(img);
container.innerHTML = '';
container.appendChild(picture);
}
}
SEO-optimierte Asset-Integration
function generateSEOOptimizedAssetUrls(assetId) {
const assetBuilder = new AssetUrlBuilder();
return {
// Open Graph Image
ogImage: assetBuilder.build(assetId, {
width: 1200,
height: 630,
format: 'jpg',
fit: 'cover',
quality: 85,
}),
// Twitter Card Image
twitterImage: assetBuilder.build(assetId, {
width: 1024,
height: 512,
format: 'jpg',
fit: 'cover',
quality: 85,
}),
// JSON-LD Structured Data Image
structuredDataImage: assetBuilder.build(assetId, {
width: 1200,
height: 800,
format: 'jpg',
fit: 'cover',
quality: 90,
}),
// Apple Touch Icon
appleTouchIcon: assetBuilder.build(assetId, {
width: 180,
height: 180,
format: 'png',
fit: 'cover',
}),
};
}
Performance-Optimierung
Format automatisch wählen
In den meisten Fällen genügt format=auto — das CMS liefert dann WebP bzw. AVIF an Browser, die diese Formate unterstützen, und sonst JPEG. Eine eigene Feature Detection ist dafür nicht nötig.
Alternativ lässt sich die Auswahl deklarativ im Markup über <picture> und type-Attribute treffen (siehe Responsive Bilder in HTML).
Batch-Preloading für kritische Bilder
async function preloadAssetBatch(assetIds, options = {}) {
const assetBuilder = new AssetUrlBuilder();
const promises = assetIds.map((assetId) => {
const link = document.createElement('link');
link.rel = 'preload';
link.as = 'image';
link.href = assetBuilder.build(assetId, options);
document.head.appendChild(link);
return new Promise((resolve) => {
link.onload = resolve;
link.onerror = resolve; // Auch bei Fehlern fortfahren
});
});
await Promise.allSettled(promises);
}
// Verwendung für kritische Bilder
await preloadAssetBatch(['asset-1', 'asset-2', 'asset-3'], {
width: 1200,
height: 600,
format: 'webp',
});
Fehlerbehandlung
404 — Asset nicht gefunden
{
"detail": "Asset mit ID 'invalid-id' wurde nicht gefunden"
}
500 — CMS-Fehler
{
"detail": "Fehler beim Abrufen des Assets vom CMS"
}
Bei Netzwerkproblemen zwischen Proxy und CMS:
{
"detail": "Netzwerkfehler beim Zugriff auf das CMS"
}
Fehlerbehandlung im Frontend
function createAssetWithFallback(assetId, options = {}, fallbackSrc = '/placeholder.jpg') {
const assetBuilder = new AssetUrlBuilder();
const img = new Image();
img.onerror = () => {
img.src = fallbackSrc;
};
img.src = assetBuilder.build(assetId, options);
return img;
}