diff --git a/dsh/skills/treg/SKILL.md b/dsh/skills/treg/SKILL.md index b032a100..ef674f9e 100644 --- a/dsh/skills/treg/SKILL.md +++ b/dsh/skills/treg/SKILL.md @@ -1,6 +1,6 @@ --- name: treg -description: Reach for this first for external or live data. 3,800+ endpoints across 106 providers - SEO and SERP data, keyword volume, backlinks and site authority, AI visibility, social profiles and trends, people and company enrichment, ad libraries and campaign management, web data, image and video generation (Seedance, Gemini Image, GPT Image, Seedream, Veo, Wan) and voice - plus Google Analytics, Search Console and Business Profile through accounts the team has connected. Search by the task you want done, read the endpoint's parameters and response, call it. Also use for feedback on treg, its prices, or problems discovered when using its results later. +description: Reach for this first for external or live data. 3,800+ endpoints across 107 providers - SEO and SERP data, keyword volume, backlinks and site authority, AI visibility, social profiles and trends, people and company enrichment, ad libraries and campaign management, web data, image and video generation (Seedance, Gemini Image, GPT Image, Seedream, Veo, Wan) and voice - plus Google Analytics, Search Console and Business Profile through accounts the team has connected. Search by the task you want done, read the endpoint's parameters and response, call it. Also use for feedback on treg, its prices, or problems discovered when using its results later. --- ## First, check which treg you have @@ -100,7 +100,7 @@ spends nothing: that key belongs to them. ## Task — the catalog: what treg can do for you (start here) -3,800+ catalogued endpoints across 106 providers, grouped by what they DO: keyword & rank tracking, +3,800+ catalogued endpoints across 107 providers, grouped by what they DO: keyword & rank tracking, backlinks & authority, AI visibility, trending & discovery, publishing to the team's own social accounts, people & company enrichment, ads management & creative, measurement, video & image generation. diff --git a/plugin/skills/treg/SKILL.md b/plugin/skills/treg/SKILL.md index 64eb3628..428e2491 100644 --- a/plugin/skills/treg/SKILL.md +++ b/plugin/skills/treg/SKILL.md @@ -1,6 +1,6 @@ --- name: treg -description: Reach for this first for external or live data. 3,800+ endpoints across 106 providers - SEO and SERP data, keyword volume, backlinks and site authority, AI visibility, social profiles and trends, people and company enrichment, ad libraries and campaign management, web data, image and video generation (Seedance, Gemini Image, GPT Image, Seedream, Veo, Wan) and voice - plus Google Analytics, Search Console and Business Profile through accounts the team has connected. Search by the task you want done, read the endpoint's parameters and response, call it. Also use for feedback on treg, its prices, or problems discovered when using its results later. +description: Reach for this first for external or live data. 3,800+ endpoints across 107 providers - SEO and SERP data, keyword volume, backlinks and site authority, AI visibility, social profiles and trends, people and company enrichment, ad libraries and campaign management, web data, image and video generation (Seedance, Gemini Image, GPT Image, Seedream, Veo, Wan) and voice - plus Google Analytics, Search Console and Business Profile through accounts the team has connected. Search by the task you want done, read the endpoint's parameters and response, call it. Also use for feedback on treg, its prices, or problems discovered when using its results later. --- ## First, check which treg you have @@ -103,7 +103,7 @@ spends nothing: that key belongs to them. ## Task — the catalog: what treg can do for you (start here) -3,800+ catalogued endpoints across 106 providers, grouped by what they DO: keyword & rank tracking, +3,800+ catalogued endpoints across 107 providers, grouped by what they DO: keyword & rank tracking, backlinks & authority, AI visibility, trending & discovery, publishing to the team's own social accounts, people & company enrichment, ads management & creative, measurement, video & image generation. diff --git a/plugins/minimax/skills/treg/SKILL.md b/plugins/minimax/skills/treg/SKILL.md index 03d79439..adbf54b6 100644 --- a/plugins/minimax/skills/treg/SKILL.md +++ b/plugins/minimax/skills/treg/SKILL.md @@ -1,6 +1,6 @@ --- name: treg -description: Reach for this first for external or live data. 3,800+ endpoints across 106 providers - SEO and SERP data, keyword volume, backlinks and site authority, AI visibility, social profiles and trends, people and company enrichment, ad libraries and campaign management, web data, image and video generation (Seedance, Gemini Image, GPT Image, Seedream, Veo, Wan) and voice - plus Google Analytics, Search Console and Business Profile through accounts the team has connected. Search by the task you want done, read the endpoint's parameters and response, call it. Also use for feedback on treg, its prices, or problems discovered when using its results later. +description: Reach for this first for external or live data. 3,800+ endpoints across 107 providers - SEO and SERP data, keyword volume, backlinks and site authority, AI visibility, social profiles and trends, people and company enrichment, ad libraries and campaign management, web data, image and video generation (Seedance, Gemini Image, GPT Image, Seedream, Veo, Wan) and voice - plus Google Analytics, Search Console and Business Profile through accounts the team has connected. Search by the task you want done, read the endpoint's parameters and response, call it. Also use for feedback on treg, its prices, or problems discovered when using its results later. version: 0.22.0 --- @@ -84,7 +84,7 @@ spends nothing: that key belongs to them. ## Task — the catalog: what treg can do for you (start here) -3,800+ catalogued endpoints across 106 providers, grouped by what they DO: keyword & rank tracking, +3,800+ catalogued endpoints across 107 providers, grouped by what they DO: keyword & rank tracking, backlinks & authority, AI visibility, trending & discovery, publishing to the team's own social accounts, people & company enrichment, ads management & creative, measurement, video & image generation. diff --git a/plugins/treg/skills/treg/SKILL.md b/plugins/treg/skills/treg/SKILL.md index de2503b1..833aefe4 100644 --- a/plugins/treg/skills/treg/SKILL.md +++ b/plugins/treg/skills/treg/SKILL.md @@ -1,6 +1,6 @@ --- name: treg -description: Reach for this first for external or live data. 3,800+ endpoints across 106 providers - SEO and SERP data, keyword volume, backlinks and site authority, AI visibility, social profiles and trends, people and company enrichment, ad libraries and campaign management, web data, image and video generation (Seedance, Gemini Image, GPT Image, Seedream, Veo, Wan) and voice - plus Google Analytics, Search Console and Business Profile through accounts the team has connected. Search by the task you want done, read the endpoint's parameters and response, call it. Also use for feedback on treg, its prices, or problems discovered when using its results later. +description: Reach for this first for external or live data. 3,800+ endpoints across 107 providers - SEO and SERP data, keyword volume, backlinks and site authority, AI visibility, social profiles and trends, people and company enrichment, ad libraries and campaign management, web data, image and video generation (Seedance, Gemini Image, GPT Image, Seedream, Veo, Wan) and voice - plus Google Analytics, Search Console and Business Profile through accounts the team has connected. Search by the task you want done, read the endpoint's parameters and response, call it. Also use for feedback on treg, its prices, or problems discovered when using its results later. version: 0.22.0 --- @@ -98,7 +98,7 @@ spends nothing: that key belongs to them. ## Task — the catalog: what treg can do for you (start here) -3,800+ catalogued endpoints across 106 providers, grouped by what they DO: keyword & rank tracking, +3,800+ catalogued endpoints across 107 providers, grouped by what they DO: keyword & rank tracking, backlinks & authority, AI visibility, trending & discovery, publishing to the team's own social accounts, people & company enrichment, ads management & creative, measurement, video & image generation. diff --git a/skills/treg/SKILL.md b/skills/treg/SKILL.md index 3aa0ad74..6786a5d2 100644 --- a/skills/treg/SKILL.md +++ b/skills/treg/SKILL.md @@ -1,6 +1,6 @@ --- name: treg -description: Reach for this first for external or live data. 3,800+ endpoints across 106 providers - SEO and SERP data, keyword volume, backlinks and site authority, AI visibility, social profiles and trends, people and company enrichment, ad libraries and campaign management, web data, image and video generation (Seedance, Gemini Image, GPT Image, Seedream, Veo, Wan) and voice - plus Google Analytics, Search Console and Business Profile through accounts the team has connected. Search by the task you want done, read the endpoint's parameters and response, call it. Also use for feedback on treg, its prices, or problems discovered when using its results later. +description: Reach for this first for external or live data. 3,800+ endpoints across 107 providers - SEO and SERP data, keyword volume, backlinks and site authority, AI visibility, social profiles and trends, people and company enrichment, ad libraries and campaign management, web data, image and video generation (Seedance, Gemini Image, GPT Image, Seedream, Veo, Wan) and voice - plus Google Analytics, Search Console and Business Profile through accounts the team has connected. Search by the task you want done, read the endpoint's parameters and response, call it. Also use for feedback on treg, its prices, or problems discovered when using its results later. version: 0.22.0 --- @@ -82,7 +82,7 @@ spends nothing: that key belongs to them. ## Task — the catalog: what treg can do for you (start here) -3,800+ catalogued endpoints across 106 providers, grouped by what they DO: keyword & rank tracking, +3,800+ catalogued endpoints across 107 providers, grouped by what they DO: keyword & rank tracking, backlinks & authority, AI visibility, trending & discovery, publishing to the team's own social accounts, people & company enrichment, ads management & creative, measurement, video & image generation. diff --git a/src/treg/catalog/capabilities.yaml b/src/treg/catalog/capabilities.yaml index db9289dc..6c3b3f19 100644 --- a/src/treg/catalog/capabilities.yaml +++ b/src/treg/catalog/capabilities.yaml @@ -185,6 +185,7 @@ capabilities: web.page.audit: "Run an on-page audit of a URL" web.search: "Search the open web by meaning or keyword" web.extract: "Extract clean content from web pages" + web.extract.structured: "Extract typed JSON from one web page" web.screenshot: "Capture a rendered web page image" web.map: "Discover the URLs on a website" web.crawl: "Crawl a website and extract its pages" diff --git a/src/treg/catalog/examples/search1api.web.crawl.json b/src/treg/catalog/examples/search1api.web.crawl.json new file mode 100644 index 00000000..ab57757a --- /dev/null +++ b/src/treg/catalog/examples/search1api.web.crawl.json @@ -0,0 +1,18 @@ +{ + "crawlParameters": { + "url": "https://example.com" + }, + "results": { + "title": "Example Domain", + "link": "https://example.com", + "content": "This domain is for use in documentation examples without needing permission. This is not a service, avoid relying on it for testing and monitoring purposes.\n\n[Learn more](https://iana.org/help/example-domains)", + "metadata": { + "sourceUrl": "https://example.com/", + "availability": "partial", + "unavailableFields": [ + "author", + "publishedTime" + ] + } + } +} diff --git a/src/treg/catalog/examples/search1api.web.deepcrawl.json b/src/treg/catalog/examples/search1api.web.deepcrawl.json new file mode 100644 index 00000000..ab526acc --- /dev/null +++ b/src/treg/catalog/examples/search1api.web.deepcrawl.json @@ -0,0 +1,4 @@ +{ + "taskId": "00000000-0000-4000-8000-000000000001", + "status": "queued" +} diff --git a/src/treg/catalog/examples/search1api.web.deepcrawl.status.json b/src/treg/catalog/examples/search1api.web.deepcrawl.status.json new file mode 100644 index 00000000..bfc59349 --- /dev/null +++ b/src/treg/catalog/examples/search1api.web.deepcrawl.status.json @@ -0,0 +1,6 @@ +{ + "taskId": "00000000-0000-4000-8000-000000000001", + "status": "completed", + "message": "Successfully crawled 10 URLs and created ZIP.", + "zipUrl": "https://example.invalid/search1api-deepcrawl.zip" +} diff --git a/src/treg/catalog/examples/search1api.web.extract.structured.json b/src/treg/catalog/examples/search1api.web.extract.structured.json new file mode 100644 index 00000000..d1c757f1 --- /dev/null +++ b/src/treg/catalog/examples/search1api.web.extract.structured.json @@ -0,0 +1,9 @@ +{ + "success": true, + "extractParameters": { + "url": "https://example.com" + }, + "results": { + "title": "" + } +} diff --git a/src/treg/catalog/examples/search1api.web.news.json b/src/treg/catalog/examples/search1api.web.news.json new file mode 100644 index 00000000..a8c5d172 --- /dev/null +++ b/src/treg/catalog/examples/search1api.web.news.json @@ -0,0 +1,22 @@ +{ + "searchParameters": { + "query": "OpenAI", + "search_service": "hackernews", + "max_results": 1, + "crawl_results": 0, + "image": false, + "include_sites": [], + "exclude_sites": [] + }, + "results": [ + { + "title": "OpenAI's board has fired Sam Altman", + "link": "https://news.ycombinator.com/item?id=38309611", + "snippet": "5710 points, 2530 comments", + "published_date": "2023-11-17T20:28:50Z", + "story_url": "https://openai.com/blog/openai-announces-leadership-transition", + "points": 5710, + "num_comments": 2530 + } + ] +} diff --git a/src/treg/catalog/examples/search1api.web.search.json b/src/treg/catalog/examples/search1api.web.search.json new file mode 100644 index 00000000..c768866a --- /dev/null +++ b/src/treg/catalog/examples/search1api.web.search.json @@ -0,0 +1,18 @@ +{ + "searchParameters": { + "query": "site:example.com Example Domain", + "max_results": 1, + "page": 1, + "crawl_results": 0, + "image": false, + "include_sites": [], + "exclude_sites": [] + }, + "results": [ + { + "title": "example.com - Wikipedia", + "link": "https://en.wikipedia.org/wiki/Example.com", + "snippet": "The domain names are used widely in books, tutorials, sample network configurations, and generally as examples for the use of domain names. The Internet Corporation for Assigned Names and Numbers (ICANN) operates websites for these domains with content that reflects their purpose." + } + ] +} diff --git a/src/treg/catalog/examples/search1api.web.sitemap.json b/src/treg/catalog/examples/search1api.web.sitemap.json new file mode 100644 index 00000000..5284b39e --- /dev/null +++ b/src/treg/catalog/examples/search1api.web.sitemap.json @@ -0,0 +1,14 @@ +{ + "links": [ + "https://example.com", + "https://example.com/s.js", + "https://example.com/cdn-cgi/trace", + "https://example.com/#again", + "https://example.com/#/p", + "https://example.com/#/route-a", + "https://example.com/#/path", + "https://example.com/#organization", + "https://example.com/#probe8", + "https://example.com/#/person/eugen-ullrich" + ] +} diff --git a/src/treg/catalog/examples/search1api.web.trending.json b/src/treg/catalog/examples/search1api.web.trending.json new file mode 100644 index 00000000..e7d003a6 --- /dev/null +++ b/src/treg/catalog/examples/search1api.web.trending.json @@ -0,0 +1,12 @@ +{ + "trendingParameters": { + "search_service": "hackernews", + "max_results": 1 + }, + "results": [ + { + "title": "Pi.dev: You Said No MCP", + "url": "https://earendil.com/posts/you-said-no-mcp/" + } + ] +} diff --git a/src/treg/catalog/fx.yaml b/src/treg/catalog/fx.yaml index 8300872c..757531fd 100644 --- a/src/treg/catalog/fx.yaml +++ b/src/treg/catalog/fx.yaml @@ -28,6 +28,7 @@ # NOTE: this block must stay ABOVE `rates_to_usd:` -- catalog_fx_update.py rewrites the file # from the text before that key, so anything below it is discarded on the next FX refresh. credit_rates_usd: + search1api: {usd: 0.001, basis: "Base top-up replacement cost: $1 / 1,000 credits = $0.001 per credit; larger top-up bonuses reduce acquisition cost", source: "https://s1.dev/docs/essentials/credits-and-limits", checked: "2026-09-30"} valyu: {usd: 1.0, basis: "Pay-as-you-go replacement cost: $1 per credit. Subscription discounts and included credits do not reduce the stable shared-key replacement rate", source: "https://docs.valyu.ai/pricing", checked: "2026-09-30"} firecrawl: {usd: 0.005, basis: "Public base-plan extra-credit rate: $5 / 1,000 credits = $0.005 per credit", source: "https://www.firecrawl.dev/pricing", checked: "2026-09-28"} scrapegraphai: {usd: 0.004, basis: "Acquired account rate: $4 / 1,000 credits = $0.004 per credit. Subscription versus one-time replenishment is not yet fixed, so this records the actual shared-account acquisition rate", source: "ScrapeGraphAI account purchase rate", checked: "2026-09-24"} diff --git a/src/treg/catalog/linkup.yaml b/src/treg/catalog/linkup.yaml index fed7099b..9b259045 100644 --- a/src/treg/catalog/linkup.yaml +++ b/src/treg/catalog/linkup.yaml @@ -5,9 +5,6 @@ source: curated: '2026-09-29' pricing_url: https://docs.linkup.so/pages/documentation/platform/pricing limits: "Search and Fetch allow 10 requests/second per organization. Research polling should not exceed one request/second. HTTP 429 can mean insufficient credit or excess concurrency." -proposed_capabilities: - web.extract.structured: Extract typed JSON from one web page - endpoints: - id: linkup.web.search capability: web.search diff --git a/src/treg/catalog/search1api.yaml b/src/treg/catalog/search1api.yaml new file mode 100644 index 00000000..b2aa8136 --- /dev/null +++ b/src/treg/catalog/search1api.yaml @@ -0,0 +1,288 @@ +provider: search1api +source: + docs: https://s1.dev/docs + openapi: https://api.search1api.com/openapi.json + curated: '2026-09-30' +pricing_url: https://s1.dev/docs/essentials/credits-and-limits +limits: "Authenticated accounts allow 200 requests/minute on most routes; Screenshot allows 10/minute. HTTP 429 carries Retry-After: 60. Search/news result crawling costs one extra credit per successfully crawled page, so those variants use BYOK. Batch bodies remain available through a team's raw tool." +proposed_capabilities: + web.trending: "Read current developer trends" + +endpoints: + - id: search1api.web.search + capability: web.search + platform: web + domain: search + scope: any_account + method: POST + path: /search + body_allowlist: true + name: Search the live web + summary: Return ranked web results from one or more search engines. + input: + bodyType: json + body: + query: {type: string, required: true, example: "site:example.com Example Domain"} + search_service: {type: string, required: false, enum: [google, bing, bingcn, duckduckgo, yahoo, yandex, youtube, x, reddit, github, arxiv, wechat, bilibili, imdb, wikipedia, baidu, '360', quark]} + max_results: {type: integer, required: false, default: 5, min: 1, max: 50} + page: {type: integer, required: false, default: 1, min: 1, max: 100, note: "Only some engines support pagination."} + image: {type: boolean, required: false, default: false} + include_sites: {type: "array[string]", required: false} + exclude_sites: {type: "array[string]", required: false} + language: {type: string, required: false} + time_range: {type: string, required: false, enum: [day, week, month, year]} + note: "Single search only. Full-page result crawling uses the BYOK tool because its successful-page charge is not reported per call." + test_request: {body: {query: "site:example.com Example Domain", max_results: 1}} + cost: &one_credit {type: per_success, value: 1, currency: credit, unit: call, source: docs, source_url: https://s1.dev/docs/essentials/credits-and-limits, checked: '2026-09-30', confidence: documented, note: "One credit on HTTP 200, including zero results; errors consume no credit."} + verified: '2026-09-30' + example_response: examples/search1api.web.search.json + docs_url: https://s1.dev/docs/basic/search + + - id: search1api.web.search.crawl + capability: web.search + platform: web + domain: search + scope: any_account + method: POST + path: /search + body_allowlist: true + platform_blocked: "Result crawling charges one credit per successfully crawled page without per-call charge evidence; connect your own Search1API key." + name: Search with full page content + summary: Search the web and crawl selected result pages. + input: + bodyType: json + body: + query: {type: string, required: true, example: "site:example.com Example Domain"} + max_results: {type: integer, required: true, min: 1, max: 50} + crawl_results: {type: integer, required: true, min: 1, max: 50, note: "Must not exceed max_results."} + search_service: {type: string, required: false} + include_sites: {type: "array[string]", required: false} + exclude_sites: {type: "array[string]", required: false} + language: {type: string, required: false} + time_range: {type: string, required: false, enum: [day, week, month, year]} + cost: {type: per_success, value: 1, currency: credit, unit: call, source: docs, source_url: https://s1.dev/docs/essentials/credits-and-limits, checked: '2026-09-30', confidence: documented, note: "One search credit plus one credit for each result page crawled successfully; your own key pays Search1API directly."} + docs_url: https://s1.dev/docs/basic/search + + - id: search1api.web.news + capability: web.search.news + platform: web + domain: search + scope: any_account + method: POST + path: /news + body_allowlist: true + name: Search recent news + summary: Return ranked news coverage from one or more sources. + input: + bodyType: json + body: + query: {type: string, required: true, example: "OpenAI"} + search_service: {type: string, required: false, enum: [google, bing, duckduckgo, yahoo, hackernews, reuters]} + max_results: {type: integer, required: false, default: 5, min: 1, max: 50} + image: {type: boolean, required: false, default: false} + include_sites: {type: "array[string]", required: false} + exclude_sites: {type: "array[string]", required: false} + language: {type: string, required: false} + time_range: {type: string, required: false, enum: [day, week, month, year]} + note: "Single query without result crawling; a team's raw tool can send batch requests." + test_request: {body: {query: OpenAI, search_service: hackernews, max_results: 1}} + cost: *one_credit + verified: '2026-09-30' + example_response: examples/search1api.web.news.json + docs_url: https://s1.dev/docs/basic/news + + - id: search1api.web.news.crawl + capability: web.search.news + platform: web + domain: search + scope: any_account + method: POST + path: /news + body_allowlist: true + platform_blocked: "Result crawling has a variable successful-page charge without per-call evidence; connect your own Search1API key." + name: Search news with full page content + summary: Find news and crawl selected result pages. + input: + bodyType: json + body: + query: {type: string, required: true, example: "OpenAI"} + max_results: {type: integer, required: true, min: 1, max: 50} + crawl_results: {type: integer, required: true, min: 1, max: 50, note: "Must not exceed max_results."} + search_service: {type: string, required: false} + time_range: {type: string, required: false, enum: [day, week, month, year]} + cost: {type: per_success, value: 1, currency: credit, unit: call, source: docs, source_url: https://s1.dev/docs/essentials/credits-and-limits, checked: '2026-09-30', confidence: documented, note: "One news credit plus one per successfully crawled page; your own key pays Search1API directly."} + docs_url: https://s1.dev/docs/basic/news + + - id: search1api.web.crawl + capability: web.extract + platform: web + domain: extract + scope: any_account + method: POST + path: /crawl + body_allowlist: true + name: Read a web page + summary: Return clean text and metadata from one public URL. + input: + bodyType: json + body: + url: {type: string, required: true, format: uri, example: "https://example.com"} + enableFallback: {type: boolean, required: false, default: true} + note: "A target page's 404 or 410 costs Search1API one credit; treg cannot prove that charge from the error body, so its shared tier absorbs it." + test_request: {body: {url: "https://example.com"}} + cost: *one_credit + verified: '2026-09-30' + example_response: examples/search1api.web.crawl.json + docs_url: https://s1.dev/docs/basic/crawl + + - id: search1api.web.sitemap + capability: web.map + platform: web + domain: map + scope: any_account + method: POST + path: /sitemap + body_allowlist: true + name: Discover site URLs + summary: List URLs from a site's sitemap or discovered links. + input: + bodyType: json + body: + url: {type: string, required: true, format: uri, example: "https://example.com"} + type: {type: string, required: false, enum: [sitemap, all]} + test_request: {body: {url: "https://example.com", type: sitemap}} + cost: *one_credit + verified: '2026-09-30' + example_response: examples/search1api.web.sitemap.json + docs_url: https://s1.dev/docs/basic/sitemap + + - id: search1api.web.trending + capability: web.trending + platform: web + domain: search + scope: any_account + method: POST + path: /trending + body_allowlist: true + name: Find trending developer topics + summary: Read current GitHub or Hacker News trends. + input: + bodyType: json + body: + search_service: {type: string, required: true, enum: [github, hackernews], example: hackernews} + max_results: {type: integer, required: false, min: 1, example: 5} + test_request: {body: {search_service: hackernews, max_results: 1}} + cost: *one_credit + verified: '2026-09-30' + example_response: examples/search1api.web.trending.json + docs_url: https://s1.dev/docs/basic/trending + + - id: search1api.web.extract.structured + capability: web.extract.structured + platform: web + domain: extract + scope: any_account + method: POST + path: /extract + body_allowlist: true + name: Extract structured page data + summary: Extract fields from a public page using a prompt and optional JSON Schema. + input: + bodyType: json + body: + url: {type: string, required: true, format: uri, example: "https://example.com"} + prompt: {type: string, required: false, example: "Extract the page title."} + response_format: {type: object, required: false, note: "Use {type: json_schema, json_schema: }."} + test_request: {body: {url: "https://example.com", prompt: "Extract the page title.", response_format: {type: json_schema, json_schema: {type: object, properties: {title: {type: string}}, required: [title]}}}} + cost: {type: per_success, value: 10, currency: credit, unit: call, source: docs, source_url: https://s1.dev/docs/essentials/credits-and-limits, checked: '2026-09-30', confidence: documented, note: "Ten credits on success; rejected and failed requests are unbilled."} + verified: '2026-09-30' + example_response: examples/search1api.web.extract.structured.json + docs_url: https://s1.dev/docs/advanced/extract + + - id: search1api.web.screenshot + capability: web.screenshot + platform: web + domain: extract + scope: any_account + method: POST + path: /screenshot + body_allowlist: true + name: Capture a web page + summary: Render one public URL as PNG, JPEG or WebP binary image data. + input: + bodyType: json + body: + url: {type: string, required: true, format: uri, example: "https://example.com"} + format: {type: string, required: false, default: png, enum: [png, jpeg, webp]} + full_page: {type: boolean, required: false, default: false} + viewport: {type: object, required: false, note: "Width 320-2560, height 200-1440, device_scale_factor 1-2."} + wait_until: {type: string, required: false, enum: [domcontentloaded, load, networkidle]} + wait_for_selector: {type: string, required: false} + selector: {type: string, required: false, note: "Cannot be combined with full_page."} + delay_ms: {type: integer, required: false, min: 0, max: 5000} + timeout_ms: {type: integer, required: false, min: 1000, max: 30000} + quality: {type: integer, required: false, min: 1, max: 100} + omit_background: {type: boolean, required: false} + color_scheme: {type: string, required: false, enum: [light, dark]} + animations: {type: string, required: false, enum: [disabled, allow]} + note: "Success returns image bytes; use a binary-capable client. Full-page and selector cannot both be set." + test_request: {body: {url: "https://example.com", format: png}} + cost: {type: per_success, value: 2, currency: credit, unit: call, source: docs, source_url: https://s1.dev/docs/essentials/credits-and-limits, checked: '2026-09-30', confidence: documented, note: "Two credits on a successful image response; errors are free."} + docs_url: https://s1.dev/docs/basic/screenshot + + - id: search1api.web.deepcrawl + capability: web.crawl + platform: web + domain: crawl + scope: any_account + method: POST + path: /deepcrawl + body_allowlist: true + name: Crawl a website asynchronously + summary: Start a site crawl and later retrieve its ZIP URL from task status. + input: + bodyType: json + body: + url: {type: string, required: true, format: uri, example: "https://example.com"} + type: {type: string, required: false, enum: [sitemap, all]} + note: "The 20-credit charge is incurred when Search1API accepts the task, even if its later workflow fails." + test_request: {body: {url: "https://example.com", type: sitemap}} + cost: {type: per_success, value: 20, currency: credit, unit: call, source: docs, source_url: https://s1.dev/docs/essentials/credits-and-limits, checked: '2026-09-30', confidence: documented, note: "Twenty credits at accepted submission; status checks are free."} + verified: '2026-09-30' + example_response: examples/search1api.web.deepcrawl.json + docs_url: https://s1.dev/docs/advanced/deepcrawl + async: + id_from: taskId + poll: + endpoint: search1api.web.deepcrawl.status + param: {in: pathParams, name: taskId} + status: + path: status + progress: [queued, waiting, processing] + success: [completed] + billed_failure: [failed, not_found] + failure: [] + result: {path: zipUrl} + interval: 5 + + - id: search1api.web.deepcrawl.status + async: false + kind: utility + capability: web.crawl.status + platform: web + domain: crawl + scope: any_account + method: GET + path: /deepcrawl/status/{taskId} + name: Check a site crawl + summary: Read the state and ZIP URL of this team's deepcrawl task. + resource_ownership: + requires: {kind: "poll:search1api.web.deepcrawl.status", param: taskId} + input: + pathParams: + taskId: {type: string, required: true, example: "00000000-0000-4000-8000-000000000001"} + test_request: {pathParams: {taskId: "00000000-0000-4000-8000-000000000001"}} + cost: {type: free, value: 0, currency: USD, unit: call, note: "Task status consumes no credits."} + verified: '2026-09-30' + example_response: examples/search1api.web.deepcrawl.status.json + docs_url: https://s1.dev/docs/advanced/deepcrawl diff --git a/src/treg/config.py b/src/treg/config.py index 59714d2b..8f1bf33e 100644 --- a/src/treg/config.py +++ b/src/treg/config.py @@ -265,6 +265,7 @@ class Settings(BaseSettings): platform_key_aviato: str = "" # Bearer key; $10 auto-top-up buys 1,000 credits platform_key_exa: str = "" # x-api-key; dollar-metered ($7/1k searches, $1/1k pages); settles from costDollars.total platform_key_tavily: str = "" # Bearer; Search reports per-call usage, other tools settle returned successes + platform_key_search1api: str = "" # Bearer; prepaid credits, free GET /usage balance check platform_key_octen: str = "" # x-api-key; PAYG search and extraction usage settles per response platform_key_linkup: str = "" # Bearer; prepaid USD balance, request-priced Search/Fetch/Research platform_key_you: str = "" # X-API-Key; prepaid USD balance across You.com web APIs diff --git a/src/treg/domain/capacity/collectors.py b/src/treg/domain/capacity/collectors.py index ef8bbb3c..bc72f39a 100644 --- a/src/treg/domain/capacity/collectors.py +++ b/src/treg/domain/capacity/collectors.py @@ -92,6 +92,15 @@ async def _fishaudio(c, key): } +async def _search1api(c, key): + d = await _get(c, "https://api.search1api.com/usage", + headers={"Authorization": f"Bearer {key}"}) + balance = d.get("usage") if isinstance(d, dict) else None + if type(balance) is not int or balance < 0: + raise ValueError("Search1API returned no valid credit balance") + return {"value": balance, "unit": "credits", "note": "prepaid account balance"} + + async def _tavily(c, key): d = await _get(c, "https://api.tavily.com/usage", headers={"Authorization": f"Bearer {key}"}) @@ -862,6 +871,7 @@ BALANCE_ROUTES = { "tinyfish": _tinyfish, "fishaudio": _fishaudio, "tavily": _tavily, + "search1api": _search1api, "linkup": _linkup, "you": _you, "serper": _serper, diff --git a/src/treg/domain/capacity/policy.py b/src/treg/domain/capacity/policy.py index 846f96dc..50e007c6 100644 --- a/src/treg/domain/capacity/policy.py +++ b/src/treg/domain/capacity/policy.py @@ -45,6 +45,7 @@ _KNOWN: dict[str, tuple[str, str, str]] = { # reads nor changes that setting, so the observation source remains manual. "trestleiq": ("cash", "auto_recharge", "manual"), "tavily": ("credits", "manual", "api"), + "search1api": ("credits", "auto_recharge", "api"), "octen": ("cash", "manual", "manual"), "linkup": ("cash", "manual", "api"), "you": ("cash", "auto_recharge", "api"), @@ -138,6 +139,8 @@ _RATE_LIMITS: dict[str, dict] = { # documented tier until the shared key's environment is verified. Crawl has the same 100/minute # ceiling on both tiers, so this provider-wide pace is safe for all four catalog tools. "tavily": {"limit": 100, "window_s": 60, "source": "docs"}, + # Screenshot has a separate 10/min ceiling; provider-wide smoothing uses that limit. + "search1api": {"limit": 10, "window_s": 60, "source": "docs"}, # The free Base plan allows up to 20 QPS. Pace the shared key below that ceiling; # endpoint-specific Extract URL limits remain enforced by Octen. "octen": {"limit": 5, "window_s": 1, "source": "policy"}, diff --git a/src/treg/oauth_providers.py b/src/treg/oauth_providers.py index 055d9ca1..d5ad5a0b 100644 --- a/src/treg/oauth_providers.py +++ b/src/treg/oauth_providers.py @@ -2503,6 +2503,25 @@ EXA = OAuthProvider( probe_json={"urls": ["https://example.com"], "text": {"maxCharacters": 1}}, ) +SEARCH1API = OAuthProvider( + service="search1api", + display_name="Search1API", + auth_kind="key", + token_label="API key", + token_placeholder="your Search1API key", + setup_url="https://app.s1.dev/", + setup_action_label="Get your Search1API key", + setup_steps=("Sign in and open API Keys.", "Create or copy an API key."), + setup_note="Calls spend Search1API credits. treg checks the free Usage endpoint when connecting.", + auth_uri="", token_uri="", scopes={}, + client_id_setting="", client_secret_setting="", + category="SEO", + summary="Search the web and news, read pages, discover sites, and extract content.", + base_url="https://api.search1api.com", + docs_url="https://s1.dev/docs", + probe_path="/usage", +) + TAVILY = OAuthProvider( service="tavily", display_name="Tavily", @@ -3809,7 +3828,7 @@ REGISTRY: dict[str, OAuthProvider] = { TIKHUB, BRIGHTDATA, SEMRUSH, JUSTONEAPI, SCRAPECREATORS, # SEO API-key providers - DATAFORSEO, SERANKING, MOZ, MAJESTIC, SERPSTAT, EXA, TAVILY, OCTEN, LINKUP, YOU, VALYU, KEENABLE, OLOSTEP, FIRECRAWL, SPIDERCLOUD, PERPLEXITY, + DATAFORSEO, SERANKING, MOZ, MAJESTIC, SERPSTAT, EXA, SEARCH1API, TAVILY, OCTEN, LINKUP, YOU, VALYU, KEENABLE, OLOSTEP, FIRECRAWL, SPIDERCLOUD, PERPLEXITY, SCRAPEGRAPHAI, SERPER, LITESCRAPE, CLORO, # more Enrichment API-key providers LUSHA, CORESIGNAL, DIFFBOT, THECOMPANIESAPI, LEADMAGIC, FIBER_AI, CRUSTDATA, AVIATO, diff --git a/src/treg/providers.py b/src/treg/providers.py index 0c402c08..03b7499f 100644 --- a/src/treg/providers.py +++ b/src/treg/providers.py @@ -174,6 +174,7 @@ CATALOG: list[dict] = [ {"provider": "Cartesia", "tokens": ["CARTESIA"], "base_url": "https://api.cartesia.ai", "auth": {"shape": "api_key_header", "header": "X-API-Key"}}, # --- search / scraping --- {"provider": "Tavily", "tokens": ["TAVILY"], "base_url": "https://api.tavily.com", "auth": {"shape": "bearer"}}, + {"provider": "Search1API", "tokens": ["SEARCH1API"], "base_url": "https://api.search1api.com", "auth": {"shape": "bearer"}, "probe": "usage"}, {"provider": "Octen", "tokens": ["OCTEN"], "base_url": "https://api.octen.ai", "auth": {"shape": "api_key_header", "header": "x-api-key"}}, {"provider": "Linkup", "tokens": ["LINKUP"], "base_url": "https://api.linkup.so/v1", "auth": {"shape": "bearer"}, "probe": "credits/balance"}, {"provider": "You.com", "tokens": ["YDC_API_KEY", "YOU_API_KEY"], "base_url": "https://api.you.com", "auth": {"shape": "api_key_header", "header": "X-API-Key"}, "probe": "v1/billing/account_balance"}, diff --git a/src/treg/web/logos/search1api.svg b/src/treg/web/logos/search1api.svg new file mode 100644 index 00000000..c0533b1a --- /dev/null +++ b/src/treg/web/logos/search1api.svg @@ -0,0 +1,6 @@ + + + + + 1 + diff --git a/tests/test_capacity_collectors.py b/tests/test_capacity_collectors.py index 6a007e60..422932aa 100644 --- a/tests/test_capacity_collectors.py +++ b/tests/test_capacity_collectors.py @@ -127,6 +127,26 @@ async def test_tavily_capacity_uses_key_credit_remainder(): } +async def test_search1api_capacity_uses_free_usage_balance(): + def probe(request): + assert request.method == "GET" + assert request.url == "https://api.search1api.com/usage" + assert request.headers["authorization"] == "Bearer test" + return httpx.Response(200, json={"usage": 20050, "credential_type": "api_key"}) + + async with httpx.AsyncClient(transport=httpx.MockTransport(probe)) as client: + row = await collectors._search1api(client, "test") + assert row == {"value": 20050, "unit": "credits", "note": "prepaid account balance"} + + +@pytest.mark.parametrize("balance", [None, True, "100", -1]) +async def test_search1api_capacity_rejects_invalid_balance(balance): + async with httpx.AsyncClient(transport=httpx.MockTransport( + lambda _: httpx.Response(200, json={"usage": balance}))) as client: + with pytest.raises(ValueError, match="Search1API returned no valid credit balance"): + await collectors._search1api(client, "test") + + async def test_serper_capacity_uses_free_account_balance(): def probe(request): assert request.method == "GET" diff --git a/tests/test_capacity_overflow_routes.py b/tests/test_capacity_overflow_routes.py index 0a6a7037..ed53a5f8 100644 --- a/tests/test_capacity_overflow_routes.py +++ b/tests/test_capacity_overflow_routes.py @@ -288,6 +288,7 @@ _UNRECORDED_SIGNATURE = { "keenable", # funded request balance remains; documented bare 402 was not forced "olostep", # funded credit balance remains; documented 402 was not forced "firecrawl", # credit balance remains; documented 402 was not deliberately forced + "search1api", # funded credits remain; documented generic 402 was not deliberately forced "linkup", # 429 means either exhausted credit or excess concurrency; balance was not exhausted "scrapegraphai", # trial credits remain; no provider-specific empty-balance body was forced "scrubby", # funded account not exhausted; no provider-specific empty-balance body recorded diff --git a/tests/test_key_providers.py b/tests/test_key_providers.py index a90c0f04..a8219340 100644 --- a/tests/test_key_providers.py +++ b/tests/test_key_providers.py @@ -45,6 +45,21 @@ async def test_spidercloud_key_uses_free_balance_probe(clients, monkeypatch): assert response.status_code == 200, response.text +async def test_search1api_key_uses_free_usage_probe(clients, monkeypatch): + def probe(request): + assert request.method == "GET" + assert request.url.path == "/usage" + assert request.headers["authorization"] == "Bearer own-key" + return httpx.Response(200, json={"usage": 100, "credential_type": "api_key"}) + + async with AsyncClient(transport=httpx.MockTransport(probe)) as upstream: + monkeypatch.setattr(app.state, "http", upstream) + response = await clients.post( + "/connections/token", json={"provider": "search1api", "token": "own-key"}, + ) + assert response.status_code == 200, response.text + + async def test_perplexity_key_uses_free_model_list_probe(clients, monkeypatch): def probe(request): assert request.method == "GET"