Dokumentacja Shareglint
Wysyłasz JSON, dostajesz adres gotowego PNG. Bez przeglądarki po Twojej stronie.
Kontrakt w formie maszynowej: /openapi.json (OpenAPI 3.1 — import do Postmana, generowanie klientów).
Pierwszy render
Potrzebujesz klucza: załóż konto (bez karty, 100 obrazków miesięcznie) — klucz pokaże się od razu po rejestracji. Trzy szablony startowe dostajesz razem z kontem, więc poniższe żądanie zadziała bez żadnej konfiguracji.
?wait=true mówi, że chcesz poczekać na obrazek zamiast odpytywać o wynik.
curl -X POST "https://shareglint.com/v1/images?wait=true" \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"template": "blog-og",
"locale": "pl",
"modifications": {
"title": "Dziesięć rzeczy, które psują sen",
"author": "Anna Kowalska",
"site": "twojblog.pl",
"accent": "#f97316"
}
}'$response = Http::withToken('sk_live_TWOJ_KLUCZ')
->post('https://shareglint.com/v1/images?wait=true', [
'template' => 'blog-og',
'locale' => 'pl',
'modifications' => [
'title' => 'Dziesięć rzeczy, które psują sen',
'author' => 'Anna Kowalska',
'site' => 'twojblog.pl',
],
]);
$url = $response->json('url');const res = await fetch('https://shareglint.com/v1/images?wait=true', {
method: 'POST',
headers: {
Authorization: 'Bearer sk_live_TWOJ_KLUCZ',
'Content-Type': 'application/json',
},
body: JSON.stringify({
template: 'blog-og',
locale: 'pl',
modifications: {
title: 'Dziesięć rzeczy, które psują sen',
author: 'Anna Kowalska',
site: 'twojblog.pl',
},
}),
});
const { url } = await res.json();import requests
res = requests.post(
"https://shareglint.com/v1/images?wait=true",
headers={"Authorization": "Bearer sk_live_TWOJ_KLUCZ"},
json={
"template": "blog-og",
"locale": "pl",
"modifications": {
"title": "Dziesięć rzeczy, które psują sen",
"author": "Anna Kowalska",
"site": "twojblog.pl",
},
},
)
url = res.json()["url"]Uwierzytelnianie
Klucz w nagłówku Authorization: Bearer sk_live_…. Klucze wydajesz i odwołujesz przez /v1/keys albo w panelu.
Pełny klucz pokazujemy dokładnie raz, przy wydaniu — w bazie trzymamy tylko jego skrót. Zgubionego nie odzyskasz, wydajesz nowy i odwołujesz stary.
Ostatniego aktywnego klucza nie da się odwołać. To celowe: bez działającego klucza nie wydasz kolejnego i zamkniesz sobie konto od środka.
Async i sync
| Tryb | Odpowiedź | Kiedy |
|---|---|---|
POST /v1/images | 202 + id | Domyślnie. Wynik odbierasz przez GET /v1/images/{id} albo webhookiem. |
POST /v1/images?wait=true | 200 + gotowy url | Gdy nie chcesz odpytywać. Render zwykle trwa około sekundy. |
Tryb synchroniczny ma własne zasoby, oddzielone od kolejki asynchronicznej — zalew zadań w tle nie zagłodzi klienta czekającego na połączeniu. Gdy mimo to nie zdążymy w oknie czasowym, dostajesz 503 z kodem sync_overloaded. Zlecenie wtedy nie przepada: w ciele jest id, więc możesz odpytać o wynik jak w trybie asynchronicznym.
Formaty plików
Dodaj format do ciała żądania: png (domyślnie), jpeg, webp albo pdf. Przy jpeg i webp quality (1–100, domyślnie 85) ustawia kompresję. Przy każdym formacie rastrowym scale wybiera gęstość pikseli: 2 (domyślnie) daje 2400×1260 dla szablonu 1200×630, 1 — dokładnie 1200×630, czyli mniejszy plik, który wciąż spełnia wymagania og:image każdego serwisu.
{ "template": "blog-og", "format": "jpeg", "quality": 80, "scale": 1, "modifications": { "title": "…" } }
pdf to jedna strona dokładnie w wymiarach szablonu w pikselach CSS (1200×630 dla szablonów wbudowanych), bez marginesów, z wydrukowanym tłem. Tekst zostaje wektorowy — ostry przy każdym powiększeniu i do zaznaczenia. quality i scale go nie dotyczą.
Format jest częścią tożsamości obrazka: te same wartości jako PNG i jako JPEG to dwa pliki i dwa rendery. Parametr, który pliku nie zmienia — quality przy PNG, scale przy PDF — nigdy nie wywoła drugiego renderu. Odpowiedź i GET /v1/images/{id} mają pole format, a adres pliku kończy się na .png, .jpg, .webp albo .pdf. W planie Free znak wodny jest w każdym formacie, także w PDF.
Idempotencja
Dołóż nagłówek Idempotency-Key (dowolny UUID). Powtórzone żądanie z tym samym kluczem i tym samym ciałem zwróci tę samą odpowiedź, bez drugiego renderu i bez drugiego odliczenia z limitu.
Ten sam klucz z innym ciałem to 422 — to prawie zawsze błąd po stronie integracji, a nie zamiar.
Niezależnie od tego identyczny render drugi raz i tak nie kosztuje: ten sam szablon z tymi samymi wartościami zwraca istniejący obrazek i nie zużywa limitu.
Błędy i limity
Pole error to stały kod maszynowy — buduj na nim swój switch. Pole message jest dla człowieka i zmienia język zależnie od nagłówka Accept-Language (obsługujemy en, pl, de, es, fr).
| Kod | error | Znaczenie |
|---|---|---|
| 401 | unauthorized | Brak klucza albo klucz nieprawidłowy lub odwołany. |
| 402 | quota_exceeded | Miesięczny limit wyczerpany. |
| 404 | — | Zasób nie istnieje na tym koncie. Cudzych zasobów nie odróżnisz od nieistniejących. |
| 422 | idempotency_key_reuse, last_key | Błąd walidacji albo konflikt. |
| 429 | — | Za dużo żądań na minutę. |
| 503 | sync_overloaded | Tryb sync nie zdążył. Zlecenie żyje, odpytaj o id. |
Jak długo żyją obrazki
Gotowe pliki nie leżą u nas wiecznie i mówimy o tym wprost, bo adres z naszej odpowiedzi trafia zwykle prosto do cudzego og:image.
| Plan | Obrazek dostępny przez |
|---|---|
| Free | 30 dni od wygenerowania |
| Starter, Pro, Scale | 365 dni od wygenerowania |
Po tym czasie znika plik i wpis w historii. Znaczy to również, że ten sam render zamówiony ponownie nie trafi już w cache i policzy się do limitu drugi raz. Jeśli obrazek ma żyć dłużej niż my go trzymamy, pobierz go do siebie od razu po wygenerowaniu — adres jest zwykłym plikiem PNG.
Własne szablony
Wysyłasz swój HTML i CSS. Pola wstawiasz jako {{ nazwa }} i deklarujesz je w fields. Niezadeklarowane pole to błąd przy zapisie, a nie puste miejsce w gotowym obrazku.
{
"name": "Karta wydarzenia",
"fields": [
"headline",
"place",
"photo"
],
"images": [
"photo"
],
"css": "body{background:#111827;color:#f9fafb;padding:72px;font-family:Roboto}",
"html": "<div class=\"fitbox\"><h1 class=\"fit\" data-autofit>{{ headline }}</h1></div><span>{{ place }}</span>"
}
Kilka rzeczy warto wiedzieć zawczasu:
- HTML jest czyszczony przy zapisie —
<script>,<iframe>i atrybutyon*znikają. Odpowiedź zawiera wersję po czyszczeniu, więc od razu widzisz, co realnie się wyrenderuje. - CSS traci
@import,@font-faceiurl()spozadata:. Zasoby z sieci i tak nie zostałyby pobrane. - Fonty wybierasz z naszej listy. Wartości pól są escapowane, więc treść użytkownika nigdy nie stanie się znacznikiem.
- Adresy obrazków pobieramy my i wklejamy jako dane — dlatego pole z obrazkiem musi być wymienione w
images.
Podgląd nie zużywa limitu, więc iterowanie nad wyglądem nic nie kosztuje.
Szablony wbudowane
Każde konto renderuje je po samym slugu, bez konfiguracji — "template": "job-posting" po prostu działa. Każde pole jest opcjonalne; accent (kolor hex) i theme (dark albo light) działają we wszystkich. Podglądy i przykłady dla każdego są w galerii szablonów.
| Slug | Pola |
|---|---|
blog-og |
title, eyebrow, author, site, date, reading_time, photo, accent, theme |
site-card |
title, description, site, url, favicon, image, accent, theme |
job-posting |
title, company, location, salary, type, eyebrow, cta, site, logo, accent, theme |
event |
name, date, venue, eyebrow, site, image, accent, theme |
podcast-episode |
title, show, episode, guest, duration, site, cover, accent, theme |
changelog |
version, title, product, highlight_1, highlight_2, highlight_3, date, site, logo, accent, theme |
docs-page |
title, description, section, product, site, logo, accent, theme |
real-estate |
price, address, city, beds, baths, area, badge, site, photo, logo, accent, theme |
product-og |
name, tagline, price, badge, site, logo, screenshot, accent, theme |
product-sale |
name, brand, price, old_price, discount, cta, site, photo, accent, theme |
course-lesson |
title, course, lesson, instructor, duration, progress, site, photo, accent, theme |
recipe |
title, category, time, servings, difficulty, author, site, photo, accent, theme |
video-thumbnail |
title, highlight, badge, channel, duration, image, avatar, accent, theme |
newsletter-issue |
title, newsletter, issue, date, excerpt, author, site, accent, theme |
quote-og |
quote, author, role, site, photo, accent, theme |
case-study |
quote, name, role, company, metric, metric_label, site, photo, logo, accent, theme |
github-repo |
repo, owner, description, stars, forks, language, site, avatar, accent, theme |
feature-announcement |
title, description, eyebrow, product, site, logo, screenshot, accent, theme |
profile-card |
name, headline, bio, handle, location, site, photo, accent, theme |
comparison |
left, right, title, left_note, right_note, accent_b, site, left_logo, right_logo, accent, theme |
stat-highlight |
value, label, source, eyebrow, site, logo, accent, theme |
GET /v1/templates/library zwraca tę samą listę w JSON-ie, z wartościami domyślnymi, przykładowymi i adresem podglądu. Szablonów wbudowanych nie edytujesz; POST /v1/templates/library/{slug}/copy zakłada z jednego z nich zwykły szablon HTML na Twoim koncie, z tymi samymi polami, który potem zmieniasz przez PATCH /v1/templates/{id}. Jeśli slug jest na Twoim koncie wolny, kopia go przejmuje, więc Twoje dotychczasowe wywołania przechodzą na Twoją wersję; jeśli nie — dostaje końcówkę -copy (albo podaj własne slug i name). Szablon konta zawsze wygrywa z wbudowanym o tym samym slugu.
Auto-dopasowanie tekstu
Prędzej czy później ktoś wstawi tytuł na 200 znaków w pole zaprojektowane na 60. Dodaj data-autofit elementowi wewnątrz kontenera .fitbox — zmniejszymy czcionkę, aż tekst się zmieści. Dolną granicę ustawiasz przez data-autofit-min.
<div class="fitbox">
<h1 class="fit" data-autofit data-autofit-min="36">{{ title }}</h1>
</div>
Edytor wizualny
Nie piszesz CSS? Złóż szablon w edytorze wizualnym w panelu: zacznij od gotowego układu, dodaj warstwy tekstu, obrazka i kształtu, przeciągnij je na miejsce (z przyciąganiem do krawędzi i środków), ustaw kroje, kolory, gradienty, zaokrąglenia i krycie, a przycisk Podgląd pokaże prawdziwy PNG wyrenderowany przez Chrome — podgląd nie zużywa limitu.
Dowolną warstwę tekstu połączysz z nazwą pola — wpisany tekst staje się wtedy przykładem i wartością domyślną pola; {{ name }} możesz też wpisać w dowolnym miejscu tekstu. Warstwa obrazka bierze albo adres obrazka przy każdym renderze (pole obrazka), albo wgrany plik. fields i images edytor deklaruje sam.
Edytor kompiluje warstwy do zwykłego HTML i CSS, które przechodzą przez ten sam sanityzator i tę samą ścieżkę renderu co szablony pisane ręcznie — wywołujesz go przez "template": "twoj-slug" jak każdy inny. Wgrane obrazki są osadzone w CSS, więc razem muszą się zmieścić w około 48 KB — edytor sam je kompresuje; zdjęcia zmienne przy każdym obrazku podawaj przez pole obrazka.
Ręczna edycja szablonu z edytora (w edytorze kodu albo przez PATCH /v1/templates) przełącza go w tryb kodu: od tej chwili to zwykły HTML i CSS, a edytor wizualny go nie otwiera.
Szablon wygenerowany przez AI
Opisz obrazek jednym-dwoma zdaniami albo wgraj zrzut ekranu projektu, który ci się podoba, a POST /v1/templates/generate odda gotowy szablon: HTML, CSS, fields, przykładowe defaults i adres darmowego podglądu. To propozycja — nic się nie zapisuje, dopóki nie wyślesz jej (bez zmian albo po poprawkach) na POST /v1/templates. W panelu przycisk Wygeneruj z AI wypełnia za ciebie edytor kodu.
curl https://shareglint.com/v1/templates/generate \
-H "Authorization: Bearer sk_live_…" \
-F description="Dark blog card, big white title, author at the bottom, purple accent" \
-F image=@design.png
Odpowiedź przechodzi dokładnie tę samą walidację i ten sam sanityzator co szablon pisany ręcznie: tylko nasze fonty, pola {{ pole }}, .fitbox/data-autofit na długie tytuły, bez skryptów i bez zewnętrznych adresów. Szablony mają 1200×630.
Tylko plany płatne (Starter 20, Pro 50, Scale 100 generowań dziennie, odnawiane o północy UTC); Free dostaje 403 plan_required, limit dzienny — 429 ai_daily_limit. Gdy zawiedzie dostawca AI, dostajesz 502 ai_failed i nic się nie liczy. Obraz to PNG, JPEG albo WebP do 4 MB, wysyłany jako multipart image; opisu ani obrazu nie trzymamy po zakończeniu żądania.
Webhooki
Podaj webhook_url przy zlecaniu renderu, a zawiadomimy Cię, gdy się skończy — zamiast odpytywania w pętli.
{
"event": "image.completed",
"id": "img_01m08s3ez1zcv28h335g9923bw",
"status": "completed",
"url": "https://cdn.yourdomain.com/renders/img_01m08s3ez1zcv28h335g9923bw.png",
"error": null,
"created_at": "2026-08-17T21:13:54+00:00"
}
Każde wywołanie ma nagłówki X-Webhook-Timestamp i X-Webhook-Signature. Sekret do weryfikacji znajdziesz w panelu albo pod GET /v1/account; wymienisz go przez POST /v1/account/webhook-secret. Podpis liczymy z obu wartości, żeby podsłuchanego wywołania nie dało się odtworzyć:
$expected = hash_hmac('sha256', $timestamp . '.' . $rawBody, $webhookSecret);
if (! hash_equals($expected, $request->header('X-Webhook-Signature'))) {
abort(400);
}
Ponawiamy pięć razy z narastającym odstępem, więc chwilowa awaria po Twojej stronie nie gubi zdarzenia. Adres w sieci prywatnej odrzucamy od razu, kodem 422 — zamiast przyjąć zlecenie, policzyć je do limitu i po cichu nie zadzwonić.
Podpisane adresy obrazków (bez kodu po stronie serwera)
Adres, który wklejasz wprost w og:image — dla stron statycznych, Webflow, Shopify, motywów Ghosta i narzędzi no-code, gdzie nie da się trzymać klucza API ani wysłać POST-a. Pola szablonu idą w query stringu, a parametr s to podpis zrobiony Twoim sekretem podpisującym. Dostępne w każdym planie, także Free.
<meta property="og:image" content="https://shareglint.com/i/acc_YOUR_ACCOUNT_ID/blog-og.png?title=Ten%20things%20that%20ruin%20your%20sleep&s=…">
Pierwsze żądanie renderuje obrazek (około sekundy), zużywa jeden render z limitu dokładnie tak jak POST /v1/images i odpowiada przekierowaniem 302 na gotowy plik. Każde kolejne żądanie o ten sam szablon z tymi samymi wartościami to 302 na istniejący plik — bez renderu i bez limitu. Przekierowanie da się cache'ować przez godzinę (sam plik przez rok), więc roboty co jakiś czas wracają przez nas — i każda taka wizyta trafia do statystyk wyświetleń.
Jak podpisać
- Ścieżka:
/i/{account_id}/{template}.{ext}—idTwojego konta (acc_…, jawne, to nie jest poświadczenie), slug szablonu i rozszerzenie, o które prosisz. Bez hosta. Rozszerzenie wybiera format:.png,.jpg,.webp(jakość 85) albo.pdf; rastry mają 2× wymiary szablonu. Rozszerzenie jest częścią podpisanej ścieżki, więc podpisz dokładnie ten adres, który publikujesz — podpis dla.pngnie otworzy.jpg. - Weź wszystkie parametry query poza
s(pola szablonu i opcjonalnelocale), posortuj je po nazwie (kolejność bajtowa), zakoduj nazwę i wartość wg RFC 3986 — wszystko pozaA–Z a–z 0–9 - _ . ~, więc spacja to%20— i złącz jakonazwa=wartośćznakiem&. - Kanon = ścieżka +
?+ złączone parametry.?zostaje nawet bez parametrów. s= pierwsze 32 znaki hex (małymi literami) zHMAC-SHA256(kanon, signing_secret)(128 bitów — przy naszych limitach żądań zgadywanie nie ma sensu, a adres jest o połowę krótszy).- Dopisz
sdo adresu. Sam adres może mieć parametry w dowolnej kolejności i spację jako+albo%20: dekodujemy go i składamy kanon sami. Wartości podpisujesz dokładnie takie, jakie wysyłasz — niczego nie przycinamy.
Sprawdź swoją implementację tym wektorem testowym, zanim wkleisz cokolwiek w og:image:
secret sign_example
path /i/acc_01k8example0000000000000000/blog-og.png
params title=Hello, world! author=Ann & Bob locale=en
canonical /i/acc_01k8example0000000000000000/blog-og.png?author=Ann%20%26%20Bob&locale=en&title=Hello%2C%20world%21
s cdc6c6ad85a071f083b5f1374b0a6d98
PHP
function signedImageUrl(string $accountId, string $template, array $params, string $secret, string $ext = 'png'): string
{
$path = "/i/{$accountId}/{$template}.{$ext}"; // png, jpg, webp or pdf
ksort($params, SORT_STRING);
$query = implode('&', array_map(
fn ($name, $value) => rawurlencode((string) $name).'='.rawurlencode((string) $value),
array_keys($params),
$params,
));
$s = substr(hash_hmac('sha256', $path.'?'.$query, $secret), 0, 32);
return 'https://shareglint.com'.$path.'?'.($query === '' ? '' : $query.'&').'s='.$s;
}
echo signedImageUrl('acc_YOUR_ACCOUNT_ID', 'blog-og', [
'title' => 'Ten things that ruin your sleep',
'author' => 'Anna Kowalska',
], getenv('SHAREGLINT_SIGNING_SECRET'));
JavaScript (Node)
import { createHmac } from 'node:crypto';
// encodeURIComponent leaves !'()* alone; RFC 3986 (and PHP's rawurlencode) does not.
const enc = (v) => encodeURIComponent(String(v))
.replace(/[!'()*]/g, (c) => '%' + c.charCodeAt(0).toString(16).toUpperCase());
export function signedImageUrl(accountId, template, params, secret, ext = 'png') {
const path = `/i/${accountId}/${template}.${ext}`; // png, jpg, webp or pdf
const query = Object.keys(params).sort()
.map((name) => `${enc(name)}=${enc(params[name])}`)
.join('&');
const s = createHmac('sha256', secret).update(`${path}?${query}`).digest('hex').slice(0, 32);
return `https://shareglint.com${path}?${query ? query + '&' : ''}s=${s}`;
}
console.log(signedImageUrl('acc_YOUR_ACCOUNT_ID', 'blog-og', {
title: 'Ten things that ruin your sleep',
author: 'Anna Kowalska',
}, process.env.SHAREGLINT_SIGNING_SECRET));
Narzędzia no-code
Podpisze wszystko, co ma funkcję HMAC-SHA256: krok z kodem JavaScript w Zapierze, Pipedream i podobnych (dostosuj JavaScript z góry), węzeł Crypto w n8n (akcja Hmac, typ SHA256, kodowanie hex — potem pierwsze 32 znaki) albo krok budowania generatora stron statycznych. Kanon składasz dokładnie jak w krokach wyżej. Nigdy nie podpisuj w JavaScripcie przeglądarki: sekret stałby się publiczny.
Warto wiedzieć
- Sekret podpisujący i
idkonta znajdziesz w panelu i podGET /v1/account(signing_secret,id). To osobny sekret niż klucze API i sekret webhooków: jego wyciek nie daje nikomu dostępu do API — ale pozwala renderować na Twój limit. - Wymienisz go przez
POST /v1/account/signing-secret. Każdy adres podpisany starym sekretem przestaje działać natychmiast, także już opublikowane. Przekierowania zapamiętane przez roboty i CDN mogą żyć jeszcze do 1 h. - Ostrzej niż w API POST: pole, którego szablon nie deklaruje, to
400— literówka nie wypuści po cichu obrazka bez tytułu. Twoje szablony przyjmująfieldsiimages; wbudowane — swoje udokumentowane pola orazaccentitheme. - Błędy to krótki
text/plain, nigdy HTML:400nieznane pole albolocale,402wyczerpany limit,403brak albo zły podpis,404nieznane konto albo szablon (nie do odróżnienia),429limit żądań,503render jeszcze trwa — ponów poRetry-After. - Limity: 120 poprawnie podpisanych żądań na minutę na konto (żądania ze złym podpisem nigdy nie liczą się do Twojego) i 600 żądań na minutę z jednego adresu IP.
Obrazki auto OG (jeden tag, bez kodu)
Dodaj domenę w panelu (albo przez POST /v1/auto-og/sites) i wstaw jeden tag do szablonu strony. Dla każdej podstrony pobieramy ją, czytamy tytuł, opis, nazwę witryny, ikonę, og:image i theme-color i renderujemy kartę szablonem site-card — albo twoim własnym.
<meta property="og:image" content="https://shareglint.com/u/acc_YOUR_ACCOUNT_ID.png?url=https%3A%2F%2Fexample.com%2Fblog%2Fhello-world">
<meta name="twitter:card" content="summary_large_image">
- Rozszerzenie wybiera format:
.png,.jpgalbo.webp. Adres strony idzie zakodowany po?url=. Podpisu nie ma: obrazki dostają tylko strony z dozwolonych domen (i ich subdomen), każdy inny host to403. - Obrazek jest w cache według tego, co jest na karcie, a nie według adresu: niezmieniona strona nigdy nie zużywa limitu drugi raz, nawet udostępniona z
?utm_source=…albo pod innym adresem z tym samym<link rel="canonical">. Stronę czytamy ponownie najwyżej co 60 min, więc nowy tytuł pojawia się najpóźniej po tym czasie. - Query string jest domyślnie odcinany (linki śledzące zamieniałyby inaczej jedną stronę w wiele renderów). Włącz
keep_querydla domeny, której strony różnią się parametrami;utm_*,fbclid,gclidi podobne odpadają i tak. - Gdy strony nie da się przeczytać (strona błędu, przekroczony czas, nie HTML, przekierowanie poza domenę), dostajesz kartę z samą nazwą witryny zamiast zepsutego obrazka — tę samą dla każdej takiej strony domeny, więc kosztuje jeden render.
- Własny szablon dostaje te z pól
title,description,site,url,favicon,image,accentitheme, które deklaruje. Szablon, kolor akcentu i motyw ustawiasz osobno dla każdej domeny. - Nowa karta zużywa jeden render z limitu, tak samo jak
POST /v1/images. Błędy to krótkie tekstytext/plain:400złyurl,402wyczerpany limit,403domena spoza listy,404nieznane konto,429limit żądań. Limity: 120 żądań na minutę na konto, 600 na adres IP.
Fragmenty dla twojej platformy:
HTML
Dowolna strona: tag w <head> każdej podstrony, z jej adresem zakodowanym po ?url=. Generatory stron statycznych wstawią go ze zmiennej z adresem strony.
<!-- In the <head> of each page, with that page's own address -->
<meta property="og:image" content="https://shareglint.com/u/acc_YOUR_ACCOUNT_ID.png?url=https%3A%2F%2Fyour-site.com%2Fpricing">
<meta name="twitter:card" content="summary_large_image">
WordPress
Dodaj do functions.php motywu potomnego albo wtyczką do fragmentów kodu. Jeśli wtyczka SEO (Yoast, Rank Math) już wypisuje og:image, wyłącz w niej obrazek, żeby strona miała tylko jeden.
// functions.php of your (child) theme, or a code-snippets plugin
add_action('wp_head', function () {
global $wp;
$page = is_singular() ? wp_get_canonical_url() : home_url(user_trailingslashit($wp->request));
$image = 'https://shareglint.com/u/acc_YOUR_ACCOUNT_ID.png?url=' . rawurlencode($page);
echo '<meta property="og:image" content="' . esc_attr($image) . '">' . "\n";
echo '<meta name="twitter:card" content="summary_large_image">' . "\n";
}, 1);
Shopify
Sklep internetowy → Motywy → Edytuj kod. Zamień linie og:image w snippets/meta-tags.liquid (albo w layout/theme.liquid). canonical_url działa na produktach, kolekcjach, stronach i wpisach bloga.
{%- comment -%} snippets/meta-tags.liquid — replace the og:image lines {%- endcomment -%}
<meta property="og:image" content="https://shareglint.com/u/acc_YOUR_ACCOUNT_ID.png?url={{ canonical_url | url_encode }}">
<meta property="og:image:secure_url" content="https://shareglint.com/u/acc_YOUR_ACCOUNT_ID.png?url={{ canonical_url | url_encode }}">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta name="twitter:card" content="summary_large_image">
Ghost
Edytuj default.hbs motywu i wstaw tagi nad {{ghost_head}}. Code injection w panelu Ghosta nie wykonuje Handlebars, więc nie wstawi adresu strony.
{{!-- default.hbs, in <head> ABOVE {{ghost_head}} --}}
<meta property="og:image" content="https://shareglint.com/u/acc_YOUR_ACCOUNT_ID.png?url={{url absolute="true"}}">
<meta name="twitter:image" content="https://shareglint.com/u/acc_YOUR_ACCOUNT_ID.png?url={{url absolute="true"}}">
<meta name="twitter:card" content="summary_large_image">
Hugo
Wstaw do partiala, który składa <head>. .Permalink to pełny adres strony, urlquery go koduje.
{{/* layouts/partials/head.html (or the <head> of baseof.html) */}}
<meta property="og:image" content="https://shareglint.com/u/acc_YOUR_ACCOUNT_ID.png?url={{ .Permalink | urlquery }}">
<meta name="twitter:card" content="summary_large_image">
Webflow
Na stronie szablonu CMS: Page settings → Custom code → Inside <head> tag, z polem Slug wstawionym w miejsce SLUG. Strony statyczne: ten sam tag z adresem strony.
<!-- CMS template page → Page settings → Custom code → Inside <head> tag.
Replace SLUG with the Slug field (“+ Add field”). -->
<meta property="og:image" content="https://shareglint.com/u/acc_YOUR_ACCOUNT_ID.png?url=https://your-site.com/blog/SLUG">
<meta name="twitter:card" content="summary_large_image">
Wix
Dla każdej strony: SEO basics → Advanced SEO → Additional tags. Po ?url= adres tej strony.
<!-- Page → SEO basics → Advanced SEO → Additional tags, per page -->
<meta property="og:image" content="https://shareglint.com/u/acc_YOUR_ACCOUNT_ID.png?url=https://your-site.com/about">
Framer
Dla każdej strony: Page settings → Custom Code → End of <head>, z adresem tej strony. Usuń własny obrazek społecznościowy strony, żeby został jeden og:image.
<!-- Page settings → Custom Code → End of <head>, per page -->
<meta property="og:image" content="https://shareglint.com/u/acc_YOUR_ACCOUNT_ID.png?url=https://your-site.com/features">
<meta name="twitter:card" content="summary_large_image">
Screenshot API — zrzuty stron
Wysyłasz publiczny adres, dostajesz PNG, JPEG, WebP albo PDF strony — sam widok albo całą przewijaną stronę, z ustawieniami urządzeń, trybem ciemnym, dodatkowym czekaniem, selektorem, na który czekamy, selektorami do ukrycia i małym filtrem reklam i banerów cookie. Tryby jak przy obrazkach: domyślnie asynchronicznie, ?wait=true, żeby poczekać (do 25 s), webhook_url, żebyśmy zawołali (screenshot.completed / screenshot.failed).
curl -X POST "https://shareglint.com/v1/screenshots?wait=true" \
-H "Authorization: Bearer sk_live_…" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com", "full_page": true, "format": "webp", "block_ads": true}'
Zrzut widoku zużywa 1 render z limitu, zrzut całej strony 2. Ten sam adres z tymi samymi opcjami w ciągu 24 h oddaje gotowy plik za darmo. Strona, która się nie uda — status 4xx/5xx, przekroczony czas, zablokowany adres — kończy się status: failed z powodem, a limit wraca. Na planie Free zrzuty mają tę samą małą plakietkę „Made with” co obrazki.
Nie masz serwera? Podpisz adres tak samo jak adresy obrazków, ze ścieżką /s/{account_id}.{ext} i opcjami w query stringu (hide po przecinku), i wstaw go w <img> albo og:image:
<img src="https://shareglint.com/s/acc_YOUR_ACCOUNT_ID.png?url=https%3A%2F%2Fexample.com&device=mobile&s=…">
- Zrzuty robi osobna pula przeglądarek w odizolowanej sieci: jedyne wyjście to proxy wyjściowe, które przepuszcza tylko adresy publiczne na portach 80 i 443. Przekierowania, ramki, obrazki i skrypty prowadzące do adresów prywatnych, pętli zwrotnej, link-local (metadane chmury) i innych wewnętrznych są odrzucane po rozwiązaniu DNS — przekierowanie na
127.0.0.1albo domena z rebindingiem kończy się tak samo jak wpisany wprost adres prywatny. - Limity: widok 320–3840 × 200–2160 px, cała strona do 16,000 px wysokości,
delaydo 10 s, 20 s na stronę.
MCP: Claude, ChatGPT, Cursor
API jest też zdalnym serwerem Model Context Protocol, więc asystent AI może za ciebie renderować obrazki, przeglądać szablony, budować podpisane adresy do og:image i sprawdzać limit. Adres (streamable HTTP): https://shareglint.com/mcp. Dwie drogi, jedno konto: aplikacje czatowe (Claude, ChatGPT) logują się przez OAuth — logujesz się do Shareglint i zatwierdzasz aplikację; narzędzia programistyczne i twój kod wysyłają klucz API w nagłówku Authorization: Bearer sk_live_…. W obu przypadkach to samo konto, ten sam limit i te same ograniczenia co /v1.
Narzędzia: render_image, get_image, list_templates, get_template, create_signed_url, get_usage, take_screenshot i generate_template (plany płatne). Błędy wracają jako {"error": kod, "message": …} z tymi samymi kodami co w API HTTP.
Claude i ChatGPT (logowanie OAuth)
Claude (przeglądarka, aplikacja na komputer i telefon): Settings → Connectors → Add custom connector, nazwa Shareglint, adres https://shareglint.com/mcp, potem Connect. Trafisz na logowanie do Shareglint i ekran zgody; po zatwierdzeniu narzędzia pojawią się w czacie. W planach Team i Enterprise konektor najpierw dodaje właściciel organizacji.
ChatGPT (przestrzenie Business, Enterprise i Edu): Settings → Connectors → Create, adres https://shareglint.com/mcp, uwierzytelnienie OAuth, potem logowanie i zgoda tak samo. Administrator przestrzeni może najpierw musieć zezwolić na własne konektory.
Połączone aplikacje widać w ustawieniach konta — każdą można odłączyć, a jej tokeny od razu przestają działać. Access token żyje dobę i odświeża się sam. Klienci OAuth znajdą resztę (dynamiczna rejestracja klienta, kod autoryzacji z PKCE) pod https://shareglint.com/.well-known/oauth-protected-resource/mcp.
Cursor, Claude Code, VS Code i kod (klucz API)
Claude Code:
claude mcp add --transport http shareglint https://shareglint.com/mcp \
--header "Authorization: Bearer sk_live_…"
Cursor (~/.cursor/mcp.json), VS Code i inne klienty, które przyjmują adres z nagłówkami:
{
"mcpServers": {
"shareglint": {
"url": "https://shareglint.com/mcp",
"headers": { "Authorization": "Bearer sk_live_…" }
}
}
}
Z kodu — konektor MCP w Claude API albo OpenAI Responses API przekazują klucz tak samo:
// Claude API (MCP connector)
"mcp_servers": [{ "type": "url", "url": "https://shareglint.com/mcp", "name": "images", "authorization_token": "sk_live_…" }]
// OpenAI Responses API
"tools": [{ "type": "mcp", "server_label": "images", "server_url": "https://shareglint.com/mcp", "headers": { "Authorization": "Bearer sk_live_…" } }]
Statystyki wyświetleń
Każdy obrazek podany przez adres — podpisany /i/… (także adresy auto OG i zrzutów ekranu) — dodaje jeden do dziennego licznika platformy, która go pobrała, rozpoznanej po User-Agencie: Facebook, LinkedIn, X, Slack, Discord, WhatsApp, Telegram, iMessage, Google, inne roboty albo przeglądarka. Podglądy linków liczymy jako udostępnienia — mniej więcej „Twój link wklejono N razy”. Zobaczysz je w panelu albo pobierzesz z GET /v1/analytics?days=30 (do 90 dni, czas UTC).
- Nie zapisujemy adresu IP ani User-Agenta — tylko dzień, platformę, szablon i liczbę.
- Liczymy tylko żądania, które do nas docierają. Przekierowanie z
/i/jest cache'owane przez godzinę (sam plik przez rok), więc roboty wracają przez nas; plik spodurlzPOST /v1/imagesidzie prosto z magazynu i się nie liczy. ŻądaniaHEADi błędy też nie.
Referencja
Poniższa lista powstaje z tej samej specyfikacji, którą serwujemy pod /openapi.json.
Opisy endpointów są po angielsku niezależnie od języka strony: specyfikacja jest kontraktem maszynowym, z którego generuje się klientów, więc ma jedną, niezmienną wersję.
Images
POST /v1/images Request a render
Asynchronous by default: you get a 202 and an `id`, then collect the result from `GET /images/{id}`. With `?wait=true` we hold the connection until the image is ready and return 200 — or 503 `sync_overloaded` if we run out of time. `format`, `quality` and `scale` are part of the cache key: the same fields as PNG and as JPEG are two files and two renders, while a parameter the format ignores (`quality` for PNG) never makes a second one.
| Parametr | Miejsce | Opis |
|---|---|---|
wait |
query | Wait for the result instead of polling. |
Idempotency-Key |
header | The same `Idempotency-Key` with the same body returns the same response, with no second render. An empty or whitespace-only header counts as no key. |
Przykładowe żądanie
{
"template": "blog-og",
"locale": "en",
"modifications": {
"eyebrow": "Health",
"title": "Ten things that ruin your sleep",
"author": "Anna Kowalska",
"site": "yourblog.com",
"accent": "#f97316",
"date": "2026-08-17",
"reading_time": 7
}
}
| Odpowiedź | Opis |
|---|---|
200 | Finished (only with `?wait=true`). |
202 | Accepted into the queue. |
402 | Monthly quota exhausted (`quota_exceeded`). |
422 | Validation error (including an unknown `format`, `quality` outside 1–100 or `scale` other than 1 or 2), or the same `Idempotency-Key` with a different body. |
503 | Sync mode ran out of time (`sync_overloaded`). The body carries `id` — nothing is lost. |
GET /v1/images Render history
Cursor pagination: pass `before` from the `next_cursor` field. Renders arriving while you page through will not duplicate results.
| Parametr | Miejsce | Opis |
|---|---|---|
limit |
query | |
before |
query | Cursor from `next_cursor`. |
| Odpowiedź | Opis |
|---|---|
200 | A page of renders, newest first. |
GET /v1/images/{id} Render status
| Parametr | Miejsce | Opis |
|---|---|---|
id |
path |
| Odpowiedź | Opis |
|---|---|
200 | The render. |
404 | No such render on this account. |
GET /i/{account}/{template}.{ext} Signed image URL (for og:image)
A URL you paste straight into `<meta property="og:image">` — no server code, no API key. Template fields go in the query string, `s` is an HMAC signature made with your `signing_secret`. The extension picks the file format: `.png`, `.jpg` (quality 85), `.webp` (quality 85) or `.pdf`; rasters are 2× the template size.
Signature: build the canonical string `/i/{account}/{template}.{ext}` (the extension you request, e.g. `/i/acc_…/blog-og.jpg`) + `?` + every query parameter except `s`, sorted by name (byte order), each name and value percent-encoded per RFC 3986 (space = `%20`), joined as `name=value` with `&` (the `?` stays even with no parameters). `s` = the first 32 lowercase hex characters of HMAC-SHA256(canonical, signing_secret). The URL itself may use any order and encoding (`+` or `%20`) — we decode it and rebuild the canonical string.
An identical render that already exists is a 302 with no quota used and no rendering. Otherwise we render synchronously, use one render of quota exactly like `POST /images`, and redirect. Redirects carry `Cache-Control: public` for up to 1 hour (never past the file's retention; the file itself is cached for a year). The short redirect lifetime is deliberate: crawlers come back through us, so every served image is counted in `GET /analytics`. Errors are short `text/plain` bodies, never HTML. Limits: 120 correctly signed requests per minute per account, 600 requests per minute per IP.
| Parametr | Miejsce | Opis |
|---|---|---|
account |
path | Your account `id` from `GET /account` (`acc_…`). Public, not a credential. |
template |
path | Template slug, e.g. `blog-og`. |
ext |
path | File format. Part of the signed path: a signature for `.png` does not open `.pdf`. |
s |
query | Signature — see above. |
locale |
query | Language for text the template adds itself. Signed like any other parameter. |
{field} |
query | Any field the template declares (`fields`, `images`, `defaults`; built-in templates: their documented fields plus `accent` and `theme`). An undeclared field is a 400. |
| Odpowiedź | Opis |
|---|---|
302 | Redirect to the finished file in the requested format. |
400 | A field the template does not declare, an unsupported `locale`, or a non-string parameter. |
402 | Monthly quota exhausted (only when a new render was needed). |
403 | Missing, malformed or invalid signature. |
404 | No such account or template. The two are indistinguishable. |
429 | Rate limit. See `Retry-After`. |
500 | The render failed. No quota is used. |
503 | The render did not finish in time. It keeps going — retry after `Retry-After`. |
Templates
GET /v1/templates List templates
| Odpowiedź | Opis |
|---|---|
200 | Templates on this account, both built-in and your own, oldest first. Not paginated. Built-in templates from the library (`GET /templates/library`) work by slug without being listed here; the first render of one adds it to this list as a read-only `engine: blade` template. |
POST /v1/templates Create a template
You send your own HTML and CSS. Fields go in as `{{ name }}` and **must be declared** in `fields` — a typo fails with a 422 on save rather than leaving a blank spot in the image.
The HTML is sanitised on save (`<script>`, `<iframe>` and `on*` attributes are dropped), and the CSS loses `@import`, `@font-face` and any `url()` outside `data:`. We store the sanitised version — the `html` field in the response returns it.
Limits: HTML and CSS up to 64 KB each, dimensions from 200 to 4000 px.
Instead of `html`/`css`/`fields` you can send `editor_model` — the layer model the dashboard's visual editor produces (copy it from `GET /templates/{id}`). We validate it, compile it to HTML and CSS, declare the fields ourselves and sanitise the result like any other template.
Przykładowe żądanie
{
"name": "Event card",
"width": 1200,
"height": 630,
"fields": [
"headline",
"place",
"photo"
],
"images": [
"photo"
],
"css": "body{background:#111827;color:#f9fafb;padding:72px;font-family:Roboto} h1{font-size:88px;font-weight:900}",
"html": "<div class=\"fitbox\"><h1 class=\"fit\" data-autofit>{{ headline }}</h1></div><img src=\"{{ photo }}\"><span>{{ place }}</span>"
}
| Odpowiedź | Opis |
|---|---|
201 | Created. |
422 | A field used in the HTML is not declared, `images` goes beyond `fields`, or a size limit was exceeded. |
GET /v1/templates/{id} Template details
| Parametr | Miejsce | Opis |
|---|---|---|
id |
path |
| Odpowiedź | Opis |
|---|---|
200 | The template, with `editor_model`. |
404 | No such template on this account. |
PATCH /v1/templates/{id} Replace a template
Replaces the whole definition. Built-in templates (`engine: blade`) cannot be edited — you get a 404. Without `editor_model`, a template made in the visual editor switches to code mode (`editor: code`).
| Parametr | Miejsce | Opis |
|---|---|---|
id |
path |
| Odpowiedź | Opis |
|---|---|
200 | Saved. |
404 | No such template, or it is built-in. |
DELETE /v1/templates/{id} Delete a template
| Parametr | Miejsce | Opis |
|---|---|---|
id |
path |
| Odpowiedź | Opis |
|---|---|
204 | Deleted. |
GET /v1/templates/library Built-in template library
Ready-made templates every account can render by slug, with no setup: `"template": "job-posting"`. A template on your account with the same slug always wins. Each entry lists its `fields` (all optional; `accent` and `theme` work everywhere), `images`, `defaults`, `example` values, a `preview_url` (a real render) and the `gallery_url` page.
| Odpowiedź | Opis |
|---|---|
200 | The library, in gallery order. |
POST /v1/templates/library/{slug}/copy Copy a built-in template into your account
Built-in templates are read-only. This creates an ordinary, editable HTML template (`engine: html`) from one of them, with the same fields — edit it afterwards with `PATCH /templates/{id}`. Fields the built-in reads in CSS (`accent`, `theme`, `accent_b`) are fixed to their default values in the copy. Without `slug`, the copy takes the library slug if it is free on your account — so existing calls switch to your version — and otherwise gets a `-copy` suffix.
| Parametr | Miejsce | Opis |
|---|---|---|
slug |
path | Library slug, e.g. `job-posting`. |
Przykładowe żądanie
{
"slug": "careers-card",
"name": "Careers card"
}
| Odpowiedź | Opis |
|---|---|
201 | The new template. |
404 | No built-in template with this slug. |
422 | `slug` is taken on this account or malformed. |
POST /v1/templates/preview Preview without using quota
Renders a saved template (`template`) **or a draft** you have not saved yet (`html`, `css`, `fields`). Creates no history entry and **uses no quota** — so refining a template costs nothing. Its own rate limit: 30 calls per minute.
Przykładowe żądanie
{
"fields": [
"title"
],
"html": "<h1>{{ title }}</h1>",
"css": "body{background:#0f172a;color:#fff;font-family:Roboto}",
"modifications": {
"title": "Preview"
}
}
| Odpowiedź | Opis |
|---|---|
200 | Preview URL. |
POST /v1/templates/generate Generate a template with AI
Paid plans only. Designs a 1200×630 template from a text `description` and/or a screenshot of a design (`image`, multipart, PNG/JPEG/WebP up to 4 MB). The answer passes the same validation and sanitiser as `POST /templates`, comes with a free preview, and is **not saved** — send `template` to `POST /templates` unchanged to keep it. Daily limits: Starter 20, Pro 50, Scale 100 (UTC day). Errors: `403 plan_required`, `429 ai_daily_limit`, `502 ai_invalid_output` (with `errors`), `502 ai_failed` (not counted), `503 ai_unavailable`. Own rate limit: 6 calls per minute.
Przykładowe żądanie
{
"description": "Dark blog card, big white title, author at the bottom, purple accent"
}
| Odpowiedź | Opis |
|---|---|
200 | Proposed template, preview and today's usage. |
403 | Free plan: `plan_required`. |
422 | Neither `description` nor a valid `image`. |
429 | Daily AI limit reached: `ai_daily_limit` with `resets_at`. |
502 | `ai_invalid_output` or `ai_failed`. |
503 | `ai_unavailable`: the feature is not enabled. |
GET /v1/fonts Available fonts
Families installed in our renderer. You cannot upload your own — the render page content security policy blocks external addresses.
| Odpowiedź | Opis |
|---|---|
200 | Available font families. |
Account
GET /v1/account/usage Quota usage
`remaining` is what is left of this month's plan quota. `credits` is the balance of purchased top-up packs (valid 12 months, used only after the monthly quota runs out). Annual plans renew the quota monthly too.
| Odpowiedź | Opis |
|---|---|
200 | The current period. |
GET /v1/account Account details
Includes `webhook_secret` — the same one we sign the `X-Webhook-Signature` header with — and `signing_secret`, which you sign image URLs (`GET /i/{account}/{template}.{ext}`) with. Unlike an API key both are symmetric: both sides need the same value, so they can be read back at any time.
| Odpowiedź | Opis |
|---|---|
200 | The account. |
POST /v1/account/webhook-secret Rotate the webhook secret
Rotation takes effect **immediately, with no grace period**: the old secret stops being valid at once, so until you paste the new one on your side you will reject our calls. We retry five times with growing backoff, so the event is not lost. This route deliberately ignores `Idempotency-Key` — the response contains a plaintext secret.
| Odpowiedź | Opis |
|---|---|
200 | The new secret. |
POST /v1/account/signing-secret Rotate the image URL signing secret
Every signed image URL issued so far stops working **immediately** — including the ones already published in `og:image` tags. That is the point: a leaked secret lets anyone render on your quota. Redirects already cached by crawlers and CDNs may live up to 1 hour. This route deliberately ignores `Idempotency-Key` — the response contains a plaintext secret.
| Odpowiedź | Opis |
|---|---|
200 | The new secret. |
GET /v1/keys List API keys
We return the prefix only — the full key cannot be recovered, the database holds just its sha256.
| Odpowiedź | Opis |
|---|---|
200 | Keys on this account. |
POST /v1/keys Issue an API key
The response carries `secret`, and this is the **only** time you will see it. This route deliberately ignores `Idempotency-Key`: storing the response would mean keeping a plaintext credential in the database.
| Odpowiedź | Opis |
|---|---|
201 | The new key. |
DELETE /v1/keys/{id} Revoke a key
The last active key cannot be revoked (422 `last_key`) — without a working key you could not issue a new one and would lock yourself out.
| Parametr | Miejsce | Opis |
|---|---|---|
id |
path |
| Odpowiedź | Opis |
|---|---|
204 | Revoked. |
422 | This is the last active key (`last_key`). |
Analytics
GET /v1/analytics View analytics
Daily counts of images we served through a URL (`GET /i/…`; auto-OG and screenshot URLs count too), by the platform that fetched them, worked out from the User-Agent: `facebook`, `linkedin`, `x`, `slack`, `discord`, `whatsapp`, `telegram`, `imessage`, `google`, `other-bot`, `browser`. Link previews (Facebook, LinkedIn, X, Slack, Discord, WhatsApp, Telegram, iMessage) are `share: true` and add up to `shares` — roughly "your link was pasted N times". We store no IP address and no User-Agent, only the counters. Files fetched straight from storage (`url` of a render) are not counted; the `/i/` redirect is cached for 1 hour so crawlers come back through us. Days without views are present with zeros. Times are UTC.
| Parametr | Miejsce | Opis |
|---|---|---|
days |
query | How many days back, today included. |
| Odpowiedź | Opis |
|---|---|
200 | Counters for the period. |
422 | `days` outside 1–90. |
Auto OG
GET /v1/auto-og/sites List allowed domains
Domains whose pages get auto OG images (`GET /u/{account}.{ext}?url=…`). Each entry covers the domain, `www.` and every subdomain. `url_prefix` is your image URL up to `?url=` — append the URL-encoded page address.
| Odpowiedź | Opis |
|---|---|
200 | Allowed domains, alphabetically. |
POST /v1/auto-og/sites Allow a domain
`host` may be a domain or a whole URL; it is stored lower-case without `www.` (`https://www.Example.com/blog` → `example.com`). IP addresses, single-label names and our own domain are rejected. At most 50 domains per account. `template` defaults to `site-card`; any template on your account works and gets the fields it declares out of `title`, `description`, `site`, `url`, `favicon`, `image`, `accent`, `theme`. `accent` (`#rrggbb`) and `theme` (`dark`/`light`) override the page; `null` = the page's `theme-color` (when it is a real colour) and the template default. `keep_query`: keep the page's query string (default `false`: dropped).
Przykładowe żądanie
{
"host": "example.com",
"template": "site-card",
"accent": "#2563eb",
"theme": "light"
}
| Odpowiedź | Opis |
|---|---|
201 | The new entry. |
422 | Invalid or duplicate `host`, unknown `template`, bad `accent`, or the domain limit reached. |
PATCH /v1/auto-og/sites/{id} Change a domain
Send only what changes (same fields as creating). `accent: null` / `theme: null` go back to the page colour and the template default. Published `/u/` URLs stay the same; the next crawler request gets a card with the new settings.
| Parametr | Miejsce | Opis |
|---|---|---|
id |
path | `aog_…` |
| Odpowiedź | Opis |
|---|---|
200 | The updated entry. |
404 | No such domain on this account. |
422 | Validation failed (see creating). |
DELETE /v1/auto-og/sites/{id} Remove a domain
From now on `/u/` URLs of its pages answer `403`. Images already rendered stay in your history until retention ends.
| Parametr | Miejsce | Opis |
|---|---|---|
id |
path |
| Odpowiedź | Opis |
|---|---|
204 | Removed. |
404 | No such domain on this account. |
GET /u/{account}.{ext} Auto OG image of a page (for og:image)
One tag for a whole site: `<meta property="og:image" content="https://shareglint.com/u/acc_…png?url=PAGE">`. No API key and no signature — only pages whose host is on your allowed domains (`/auto-og/sites`) get images, any other host is a `403`.
We fetch the page (at most 5 s and 1.5 MB, private addresses and redirects off your domain refused), read its title, description, site name, icon, `og:image`, `theme-color` and canonical URL, and render a card with the domain's template. The query string is dropped unless the domain has `keep_query`; `utm_*`, `fbclid`, `gclid` and similar always are. What we read from a page is kept for 60 minutes. A page that cannot be read (error status, timeout, not HTML) gets a card with just the site name — never a broken image.
Caching and quota work like `GET /i/…`: the image is cached by the fields on the card, so an unchanged page is a `302` with no quota used, whatever address or tracking parameters it is shared with. A new card uses one render. Errors are short `text/plain` bodies. Limits: 120 requests per minute per account, 600 per IP.
| Parametr | Miejsce | Opis |
|---|---|---|
account |
path | Your account `id` (`acc_…`). Public, not a credential. |
ext |
path | File format. |
url |
query | Absolute http(s) address of the page, URL-encoded. |
| Odpowiedź | Opis |
|---|---|
302 | Redirect to the finished image. |
400 | `url` missing or not an absolute http(s) address. |
402 | Quota exhausted (only when a new card was needed). |
403 | The page's domain is not on your allowed domains. |
404 | No such account, or the domain's template was deleted. |
429 | Rate limit. See `Retry-After`. |
500 | The render failed. No quota is used. |
503 | The render did not finish in time. It keeps going — retry after `Retry-After`. |
Screenshots
POST /v1/screenshots Take a screenshot of a web page
Asynchronous by default, exactly like `POST /images`: 202 with an `id`, then `GET /screenshots/{id}` (or `GET /images/{id}` — screenshots share the render history). With `?wait=true` we hold the connection for up to 25 s and return 200, or 503 `sync_overloaded` with the `id`.
Pricing: a viewport screenshot uses 1 render of quota, a full-page one 2. The same URL with the same options within 24 h returns the existing file and uses nothing. A page that fails (4xx/5xx status, timeout, blocked address) gives `status: failed` with a reason in `error`, and the quota is returned. On the Free plan the screenshot carries the same small badge as images (bottom right; on a full page, at the bottom of the page).
The browser runs in an isolated network and reaches the internet only through an egress proxy that allows public addresses on ports 80 and 443 — redirects, frames and subresources pointing at private, loopback, link-local or metadata addresses are refused. Webhooks for screenshots carry the events `screenshot.completed` / `screenshot.failed`.
| Parametr | Miejsce | Opis |
|---|---|---|
wait |
query | Wait for the result instead of polling. |
Idempotency-Key |
header | The same key with the same body returns the same response, with no second screenshot. |
Przykładowe żądanie
{
"url": "https://example.com",
"full_page": true,
"format": "webp",
"block_ads": true
}
| Odpowiedź | Opis |
|---|---|
200 | Finished (only with `?wait=true`). |
202 | Accepted into the queue. |
402 | Not enough quota for this screenshot (`quota_exceeded`). |
422 | Validation error: missing or non-public `url`, viewport out of range, unknown `device`, `delay` too long, an unsafe selector. |
500 | With `?wait=true`: the page could not be captured (`status: failed`, reason in `error`). No quota is used. |
503 | Sync mode ran out of time (`sync_overloaded`). The body carries `id`. |
GET /v1/screenshots/{id} Screenshot status
| Parametr | Miejsce | Opis |
|---|---|---|
id |
path |
| Odpowiedź | Opis |
|---|---|
200 | The screenshot (`kind: screenshot`). |
404 | No such render on this account. |
GET /s/{account}.{ext} Signed screenshot URL (for <img> or og:image)
A screenshot as a plain URL, no API key: `/s/{account}.png?url=https%3A%2F%2Fexample.com&full_page=true&s=SIGNATURE`. Options are the `POST /screenshots` fields as query parameters (`hide` comma-separated); the extension picks the format. The signature is the same as for `GET /i/…` with the path `/s/{account}.{ext}`: canonical string = path + `?` + every parameter except `s`, sorted by name, RFC 3986-encoded, joined with `&`; `s` = first 32 hex chars of HMAC-SHA256(canonical, signing_secret).
A screenshot of the same URL and options from the last 24 h is a 302 with no quota used. Otherwise we capture synchronously (up to 25 s), charge like `POST /screenshots` and redirect. Redirects are cached for up to 1 hour; errors are short `text/plain` bodies. Same rate limits as `GET /i/…`.
| Parametr | Miejsce | Opis |
|---|---|---|
account |
path | Your account `id` (`acc_…`). Public, not a credential. |
ext |
path | File format. Part of the signed path. |
url |
query | The page, URL-encoded. |
s |
query | Signature — see above. |
{option} |
query | Any `POST /screenshots` option: `full_page`, `viewport_width`, `viewport_height`, `device`, `dark_mode`, `delay`, `wait_for`, `hide`, `block_ads`, `scale`, `quality`. Anything else is a 400. |
| Odpowiedź | Opis |
|---|---|
302 | Redirect to the finished file. |
400 | Invalid or non-public `url`, an invalid option, or an unknown parameter. |
402 | Quota exhausted (only when a new screenshot was needed). |
403 | Missing, malformed or invalid signature. |
404 | No such account. |
429 | Rate limit. See `Retry-After`. |
502 | The page could not be captured; the reason follows. No quota is used. |
503 | The screenshot did not finish in time. It keeps going — retry after `Retry-After`. |