Skip to main content

Jina AI Plugin

The Jina AI plugin provides web search with LLM-optimized results and content extraction that converts any web page into clean markdown. It communicates directly with Jina's Reader and Search APIs using plain fetch() calls.

Source: packages/plugins/jina/src/jina.plugin.ts

Overview

PropertyValue
Plugin IDjina
Categorycontent-extractor
Capabilitiessearch, content-extractor
Version1.0.0
Configuration Modehybrid
Auto-enableNo
SDKNone (plain fetch())

Despite having its primary category as content-extractor, the plugin implements both ISearchPlugin and IContentExtractorPlugin, making it usable as either a search provider or a content extractor.

Architecture

Configuration

Settings Schema

SettingTypeRequiredEnv VariableDescription
apiKeystringYesPLUGIN_JINA_API_KEYYour Jina API key. Marked as secret.

The Jina plugin has a minimal settings schema -- only an API key is required. There are no additional configuration options exposed to users.

API Endpoints

The plugin uses two Jina API endpoints:

EndpointURLPurpose
Search APIhttps://s.jina.ai/Web search with structured results
Reader APIhttps://r.jina.ai/Content extraction from URLs

Search Capability

Request Format

Search requests are sent as POST to the Search API with a JSON body:

const body = {
q: options.query, // Search query
num: options.limit, // Number of results (optional)
gl: options.region, // Geographic location (optional)
hl: options.language // Language hint (optional)
};

Headers

HeaderValuePurpose
Acceptapplication/jsonRequest JSON response
Content-Typeapplication/jsonJSON request body
X-Respond-Withno-contentSkip content extraction in search results
AuthorizationBearer <apiKey>Authentication
X-SiteFirst includeDomains entryRestrict search to a specific domain

The X-Respond-With: no-content header tells Jina to return search results without extracting full page content, reducing latency and cost.

Response Mapping

interface JinaSearchResult {
title: string;
description?: string;
url: string;
content?: string;
date?: string;
usage?: { tokens: number };
}

Each result is mapped to the standard SearchResult:

FieldSource
titleresult.title
urlresult.url
snippetresult.description
position1-based index
publishedDateresult.date
sourceHostname extracted from result.url

Timeout

Search requests use a 30-second AbortSignal timeout.

Content Extraction Capability

Single URL Extraction

The extract() method calls the Reader API to convert a web page into markdown:

const result = await jinaPlugin.extract({
url: 'https://example.com/article',
settings: { apiKey: 'jina-...' },
includeImages: true,
includeLinks: true,
timeout: 30000
});

Reader API Response

interface JinaReaderResponse {
code: number;
status: number;
data: {
title: string;
description?: string;
url: string;
content: string; // Clean markdown content
publishedTime?: string;
images?: Record<string, string>; // { alt: src }
links?: Record<string, string>; // { text: href }
metadata?: Record<string, string>;
warning?: string;
usage?: { tokens: number };
};
}

Extraction Result Fields

FieldDescription
contentClean markdown extracted from the page
markdownSame as content (Jina returns markdown natively)
titlePage title
finalUrlResolved URL if the page redirected
imagesArray of { src, alt } objects (when includeImages is not false)
linksArray of { href, text, isExternal } objects (when includeLinks is not false)
metadataDescription, published date, and language when available
wordCountCalculated from content.split(/\s+/)
readingTimeEstimated reading time in minutes (wordCount / 200)

Batch Extraction

The extractBatch() method processes URLs in batches of 5 with a 100ms delay between batches to avoid overwhelming the API:

const batchSize = 5;
for (let i = 0; i < urls.length; i += batchSize) {
const batch = urls.slice(i, i + batchSize);
const batchResults = await Promise.all(batch.map((url) => this.extract({ url, ...options })));
results.push(...batchResults);

if (i + batchSize < urls.length) {
await new Promise((resolve) => setTimeout(resolve, 100));
}
}

Supported Formats

getSupportedFormats(): readonly ('text' | 'html' | 'markdown')[] {
return ['text', 'markdown'];
}

Jina returns both text and markdown formats, making it particularly useful for content that needs to preserve structure.

Error Handling

ScenarioBehavior
Missing API keyThrows Error at search time; returns { success: false } at extraction time.
Search API errorLogs error, re-throws.
Non-OK HTTP (search)Throws Error with status code and status text.
Non-OK HTTP (extract)Returns { success: false, error: '...' }.
Empty contentReturns { success: false, error: 'No content returned' }.
TimeoutControlled by AbortSignal.timeout() (30s default for search, configurable for extract).

Rate Limits

The getRateLimitInfo() method reports the rate limit period as minute (distinct from most other plugins that report month), reflecting Jina's per-minute rate limiting model. Actual limits depend on your Jina plan.

Lifecycle

MethodBehavior
onLoad(context)Stores plugin context for logging.
onUnload()Clears stored context.
healthCheck()Returns healthy.
isAvailable()Returns true.
canExtract(url)Returns true for http: and https: URLs.

Usage in the Platform

During work generation, Jina can serve as:

  1. Search provider -- Finds relevant web pages about work items with LLM-optimized result snippets.
  2. Content extractor -- Converts discovered pages into clean markdown for enriching item descriptions, stripping ads, navigation, and other noise.