Images
Public CDN endpoints for team and region logos — cached, immutable, no auth required.
The Mr. Doge platform serves team and region logos at stable public URLs.
No API key, no auth header, no SDK call — render them directly from <img>
or <Image> tags.
Endpoints
| Resource | URL pattern |
|---|---|
| Team logo | https://api.mrdoge.co/images/teams/<teamId>.png |
| Region flag | https://api.mrdoge.co/images/regions/<regionId>.png |
Both return 200 OK with Content-Type: image/png when the image exists.
Caching
Every response sets:
Cache-Control: public, max-age=31536000, immutableThat's one year, immutable — safe to cache aggressively on CDNs, service workers, and browser caches. The IDs are stable identifiers, so an update to a team's logo will only land when a new image is uploaded server-side (which happens infrequently and is treated as a new asset).
Use with the SDK
The Team and Region shapes returned by the SDK don't include the image
URL directly — construct it from the id:
import { MrDoge } from "@mrdoge/node";
const mrdoge = new MrDoge({ apiKey: process.env.MRDOGE_API_KEY! });
const teams = await mrdoge.teams.list({ sports: ["soccer"], limit: 20 });
for (const team of teams) {
console.log(`https://api.mrdoge.co/images/teams/${team.id}.png`);
}A small helper keeps it tidy:
export function teamLogo(id: number) {
return `https://api.mrdoge.co/images/teams/${id}.png`;
}
export function regionFlag(id: number) {
return `https://api.mrdoge.co/images/regions/${id}.png`;
}In your app
<img
src={`https://api.mrdoge.co/images/teams/${team.id}.png`}
alt={team.name}
width={32}
height={32}
/>Plain <img> tags work everywhere. The 1-year cache header means
subsequent loads come from the browser cache.
Allow the host in next.config.ts:
import type { NextConfig } from "next";
const config: NextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "api.mrdoge.co",
pathname: "/images/**",
},
],
},
};
export default config;Then use next/image:
import Image from "next/image";
<Image
src={`https://api.mrdoge.co/images/teams/${team.id}.png`}
alt={team.name}
width={32}
height={32}
/>Next.js will pull the image through its optimization pipeline and serve WebP/AVIF where supported.
import { Image } from "react-native";
// or, recommended: expo-image
// import { Image } from "expo-image";
<Image
source={{ uri: `https://api.mrdoge.co/images/teams/${team.id}.png` }}
style={{ width: 32, height: 32 }}
/>expo-image honours Cache-Control properly and dedupes concurrent loads
of the same URL — recommended over react-native's built-in Image for
production apps.
Missing images
Not every team or region has a logo in the catalog. If the image is missing, the endpoint returns a non-200 status. Handle it with a fallback:
<img
src="https://api.mrdoge.co/images/teams/14.png"
alt="Liverpool"
onerror="this.src='/fallback-team.png'"
/>const [src, setSrc] = useState(`https://api.mrdoge.co/images/teams/${team.id}.png`);
<img
src={src}
alt={team.name}
onError={() => setSrc("/fallback-team.png")}
/>A colored circle with the team's initials is a common fallback — works without needing a generic asset:
function TeamLogo({ team }: { team: { id: number; name: string } }) {
const [failed, setFailed] = useState(false);
if (failed) {
return (
<div className="initials-circle">
{team.name.split(" ").map((w) => w[0]).join("").slice(0, 2)}
</div>
);
}
return (
<img
src={`https://api.mrdoge.co/images/teams/${team.id}.png`}
alt={team.name}
onError={() => setFailed(true)}
/>
);
}What you don't get
- No on-the-fly resizing. The endpoint serves the source PNG as-is.
For responsive sizes, run images through your own optimizer (
next/image, Cloudflare Images, imgix, etc.). - No format negotiation. PNG only. Use your image-optimization layer to convert to WebP/AVIF.
- No competition logos. Only
teamsandregionshave image endpoints. For competition branding, use the region's flag or the home team's logo as a contextual fallback. - No player headshots. Player references inside
MatchStatsare name-only — no per-player imagery.
Performance notes
- CDN-cached. Cached at the edge in front of
api.mrdoge.coand at the browser/SW layer. Cold-fetch is a single round-trip; subsequent loads come from cache. - No auth round-trip. The endpoint is excluded from API-key auth
middleware — no
Authorizationheader required. - Concurrent dedup. When many UI cards mount simultaneously and
request the same URL, both browsers and
expo-imagededupe to a single fetch.