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.