OpenLink

Error Handling

Understanding and handling errors from the OpenLink API.

OpenLink returns structured error responses with consistent formatting across all endpoints.

Error Response Format

{
  "error": "Human-readable error message",
  "code": "ERROR_CODE",
  "message": "Additional context (optional)",
  "docs": "https://openlink.sh/docs#relevant-section"
}

Error Codes

Preview API Errors

CodeHTTP StatusDescription
MISSING_URL400URL parameter not provided
INVALID_URL400URL format is invalid
INVALID_TTL400TTL outside valid range
FETCH_ERROR500Failed to fetch from origin

Favicon API Errors

CodeHTTP StatusDescription
MISSING_DOMAIN400Domain parameter not provided

Note: Favicon API returns generated fallback instead of errors for fetch failures.

Handling Errors

JavaScript

async function getPreview(url: string) {
  const res = await fetch(`/api/preview?url=${encodeURIComponent(url)}`)

  if (!res.ok) {
    const error = await res.json()
    throw new Error(error.message || error.error)
  }

  return res.json()
}

try {
  const data = await getPreview("https://example.com")
} catch (error) {
  console.error("Preview failed:", error.message)
}

React

function Preview({ url }: { url: string }) {
  const [data, setData] = useState(null)
  const [error, setError] = useState(null)

  useEffect(() => {
    fetch(`/api/preview?url=${encodeURIComponent(url)}`)
      .then(async res => {
        if (!res.ok) {
          const err = await res.json()
          throw new Error(err.error)
        }
        return res.json()
      })
      .then(result => setData(result.data))
      .catch(err => setError(err.message))
  }, [url])

  if (error) return <div>Error: {error}</div>
  if (!data) return <div>Loading...</div>

  return <div>{data.title}</div>
}

With Error Boundaries

import { ErrorBoundary } from "react-error-boundary"

function ErrorFallback({ error }: { error: Error }) {
  return (
    <div className="p-4 bg-red-50 text-red-600 rounded">
      Failed to load preview: {error.message}
    </div>
  )
}

function App() {
  return (
    <ErrorBoundary FallbackComponent={ErrorFallback}>
      <Preview url="https://example.com" />
    </ErrorBoundary>
  )
}

Common Issues

URL Must Include Protocol

// Wrong
fetch("/api/preview?url=example.com")

// Correct
fetch("/api/preview?url=https://example.com")

URL Must Be Encoded

// Wrong
fetch(`/api/preview?url=https://example.com?foo=bar`)

// Correct
fetch(`/api/preview?url=${encodeURIComponent("https://example.com?foo=bar")}`)

TTL Format

// Wrong
fetch("/api/preview?url=https://example.com&ttl=3600")

// Correct
fetch("/api/preview?url=https://example.com&ttl=1h")

Timeout Handling

The API has built-in timeouts:

OperationTimeout
HTML fetch5 seconds
Icon fetch3 seconds
Manifest fetch3 seconds

If a site is slow, you may see partial data or fallbacks.

Client-Side Timeout

Add your own timeout for safety:

async function fetchWithTimeout(url: string, timeout = 10000) {
  const controller = new AbortController()
  const timeoutId = setTimeout(() => controller.abort(), timeout)

  try {
    const response = await fetch(url, { signal: controller.signal })
    clearTimeout(timeoutId)
    return response
  } catch (error) {
    clearTimeout(timeoutId)
    throw error
  }
}

Graceful Degradation

Design your UI to handle missing data:

function LinkCard({ url }: { url: string }) {
  const { data, error, loading } = usePreview(url)
  const domain = new URL(url).hostname

  return (
    <div className="border rounded p-4">
      <div className="flex items-center gap-2 mb-2">
        <img
          src={`/api/favicon?domain=${domain}`}
          alt=""
          className="w-4 h-4"
          onError={(e) => e.currentTarget.style.display = "none"}
        />
        <span className="text-sm text-gray-500">
          {data?.siteName || domain}
        </span>
      </div>

      <h3 className="font-medium">
        {loading ? "Loading..." : data?.title || url}
      </h3>

      {data?.description && (
        <p className="text-gray-600 text-sm mt-1">
          {data.description}
        </p>
      )}
    </div>
  )
}

Rate Limiting

The hosted API does not currently enforce strict rate limits, but please be respectful:

  • Cache responses client-side
  • Use appropriate TTLs
  • Don't make parallel requests for the same URL
  • Contact us for high-volume usage

On this page