feat(catalog): add TinyFish tools

This commit is contained in:
shehjad-dev
2026-09-23 18:39:57 +06:00
parent d42bee1ece
commit 5ed9ab31ee
29 changed files with 737 additions and 18 deletions
+12 -3
View File
@@ -243,6 +243,13 @@ Regenerate via `scripts/build-map.py`.
| `src/treg/catalog/examples/fishaudio.tts.s2-1-pro.json` | architecture/catalog.md |
| `src/treg/catalog/examples/fishaudio.voices.create.json` | architecture/catalog.md |
| `src/treg/catalog/examples/fishaudio.voices.discover.json` | architecture/catalog.md |
| `src/treg/catalog/examples/tinyfish.web.agent.run.cancel.json` | architecture/catalog.md |
| `src/treg/catalog/examples/tinyfish.web.agent.run.get.json` | architecture/catalog.md |
| `src/treg/catalog/examples/tinyfish.web.agent.run.json` | architecture/catalog.md |
| `src/treg/catalog/examples/tinyfish.web.fetch.json` | architecture/catalog.md |
| `src/treg/catalog/examples/tinyfish.web.search.json` | architecture/catalog.md |
| `src/treg/catalog/examples/tinyfish.web.search.news.json` | architecture/catalog.md |
| `src/treg/catalog/examples/tinyfish.web.search.publications.json` | architecture/catalog.md |
| `src/treg/catalog/financialdatasets.yaml` | architecture/catalog.md |
| `src/treg/catalog/fishaudio.yaml` | architecture/catalog.md |
| `src/treg/catalog/fx.yaml` | architecture/catalog.md |
@@ -274,6 +281,7 @@ Regenerate via `scripts/build-map.py`.
| `src/treg/catalog/serpapi.yaml` | architecture/catalog.md |
| `src/treg/catalog/tavily.yaml` | architecture/catalog.md, architecture/money.md |
| `src/treg/catalog/tikhub.extended.yaml` | architecture/catalog.md |
| `src/treg/catalog/tinyfish.yaml` | architecture/catalog.md, architecture/money.md |
| `src/treg/catalog/tomba.yaml` | architecture/catalog.md, architecture/money.md |
| `src/treg/catalog/trestleiq.yaml` | architecture/catalog.md |
| `src/treg/catalog/trykitt.yaml` | architecture/catalog.md |
@@ -534,6 +542,7 @@ Regenerate via `scripts/build-map.py`.
| `tests/test_tag_billing.py` | architecture/proxy-model.md |
| `tests/test_tag_billing_adversarial.py` | architecture/proxy-model.md |
| `tests/test_team_limit.py` | architecture/multi-tenancy.md |
| `tests/test_tinyfish.py` | architecture/catalog.md, architecture/money.md, ops/capacity.md |
| `tests/test_token_revocation.py` | architecture/multi-tenancy.md |
## Fragment → sources
@@ -543,7 +552,7 @@ Regenerate via `scripts/build-map.py`.
| `architecture/ads-conversions.md` | `adsconv.py`, `signup.py`, `adtrack.js`, `gtag.js` |
| `architecture/archive.md` | `archive.py`, `hunter.yaml`, `results.py`, `0031_archive_result_admission.py`, `test_cache_result_admission.py`, `archive_bodies.py`, `config.py`, `object_store.py`, `0032_archive_body_storage.py`, `test_archive_r2.py`, `fake_object_store.py`, `smoke_archive_r2.py`, `0002_archive_tables.py`, `0003_callrecord_cached.py`, `0004_archivekey_request_shape.py`, `0011_callrecord_archive_link.py`, `service.py`, `settle.py`, `0039_archive_own_key_and_repeat_pricing.py`, `backfill_call_archive_links.py`, `api.py`, `bootstrap.py`, `admin.py`, `asynctasks.py` |
| `architecture/auth-secrets.md` | `injectors.py`, `ssrf.py`, `crypto.py`, `oauth.py`, `__init__.py`, `authorization.py`, `oauth_flow.py`, `refresh.py`, `oauth_exchange.py`, `oauth_refresh.py`, `oauth_providers.py`, `session.js`, `keys.js`, `TeamPage.vue`, `health.py`, `connect.py`, `connections.py`, `resources.py`, `__init__.py`, `bindings.py`, `bundles.py`, `api_keys.py`, `access.py`, `api_keys.py`, `test_api_keys.py`, `test_oauth_refresh.py`, `test_financialdatasets.py`, `test_key_providers.py`, `config.py` |
| `architecture/catalog.md` | `fishaudio.yaml`, `fishaudio.tts.s2-1-pro.json`, `fishaudio.voices.create.json`, `fishaudio.voices.discover.json`, `provider_resources.py`, `provider_resources.py`, `provider_resources.py`, `tavily.yaml`, `exa.yaml`, `anyapi.extended.yaml`, `adyntel.yaml`, `adyntel.meta-ads.library.advertiser.json`, `adyntel.meta-ads.library.search.json`, `adyntel.linkedin.search.ads.company.json`, `adyntel.linkedin.search.ads.keyword.json`, `adyntel.google.ads.transparency.json`, `adyntel.tiktok-ads.library.search.company.json`, `adyntel.google.domain.keywords.overview.json`, `adyntel.svg`, `trestleiq.yaml`, `financialdatasets.yaml`, `test_financialdatasets.py`, `quickenrich.yaml`, `influencersclub.yaml`, `quickenrich.extended.yaml`, `trykitt.yaml`, `contracts.yaml`, `millionverifier.yaml`, `adapters.yaml`, `prospeo.yaml`, `test_route_cost_ceiling.py`, `tomba.yaml`, `__init__.py`, `contracts.py`, `paths.py`, `plan.py`, `synthetic.py`, `route.py`, `test_routing.py`, `catalog-drift.yml`, `catalog_drift.py`, `catalog_ingest.py`, `catalog_validate.py`, `aliases.yaml`, `fx.yaml`, `cloro.yaml`, `aviato.yaml`, `crustdata.yaml`, `google-search-console.yaml`, `google-search-console.extended.yaml`, `google-tag-manager.yaml`, `google-tag-manager.extended.yaml`, `instagram.yaml`, `instagram.extended.yaml`, `justoneapi.extended.yaml`, `minimax.yaml`, `apify.yaml`, `brightdata.yaml`, `companyenrich.yaml`, `oceanio.yaml`, `akta.extended.yaml`, `dataforseo.yaml`, `dataforseo.extended.yaml`, `test_dataforseo_constraints.py`, `scrapecreators.yaml`, `scrapecreators.extended.yaml`, `serpapi.yaml`, `serpapi.extended.yaml`, `diffbot.yaml`, `diffbot.extended.yaml`, `tikhub.extended.yaml`, `lusha.extended.yaml`, `openrouter.yaml`, `openrouter.extended.yaml`, `replicate.yaml`, `replicate.extended.yaml`, `reapi.yaml`, `piapi.yaml`, `__init__.py`, `store.py`, `hunter.yaml`, `mcp.py`, `settlement.py`, `stats.py`, `catalog_observations.py`, `catalog_stats.py`, `0038_endpoint_day_stats.py`, `catalog.py`, `test_aigc_pr_b.py`, `test_catalog_api.py`, `test_catalog_validate.py` |
| `architecture/catalog.md` | `fishaudio.yaml`, `fishaudio.tts.s2-1-pro.json`, `fishaudio.voices.create.json`, `fishaudio.voices.discover.json`, `provider_resources.py`, `provider_resources.py`, `provider_resources.py`, `tavily.yaml`, `tinyfish.yaml`, `tinyfish.web.search.json`, `tinyfish.web.search.news.json`, `tinyfish.web.search.publications.json`, `tinyfish.web.fetch.json`, `tinyfish.web.agent.run.json`, `tinyfish.web.agent.run.get.json`, `tinyfish.web.agent.run.cancel.json`, `test_tinyfish.py`, `exa.yaml`, `anyapi.extended.yaml`, `adyntel.yaml`, `adyntel.meta-ads.library.advertiser.json`, `adyntel.meta-ads.library.search.json`, `adyntel.linkedin.search.ads.company.json`, `adyntel.linkedin.search.ads.keyword.json`, `adyntel.google.ads.transparency.json`, `adyntel.tiktok-ads.library.search.company.json`, `adyntel.google.domain.keywords.overview.json`, `adyntel.svg`, `trestleiq.yaml`, `financialdatasets.yaml`, `test_financialdatasets.py`, `quickenrich.yaml`, `influencersclub.yaml`, `quickenrich.extended.yaml`, `trykitt.yaml`, `contracts.yaml`, `millionverifier.yaml`, `adapters.yaml`, `prospeo.yaml`, `test_route_cost_ceiling.py`, `tomba.yaml`, `__init__.py`, `contracts.py`, `paths.py`, `plan.py`, `synthetic.py`, `route.py`, `test_routing.py`, `catalog-drift.yml`, `catalog_drift.py`, `catalog_ingest.py`, `catalog_validate.py`, `aliases.yaml`, `fx.yaml`, `cloro.yaml`, `aviato.yaml`, `crustdata.yaml`, `google-search-console.yaml`, `google-search-console.extended.yaml`, `google-tag-manager.yaml`, `google-tag-manager.extended.yaml`, `instagram.yaml`, `instagram.extended.yaml`, `justoneapi.extended.yaml`, `minimax.yaml`, `apify.yaml`, `brightdata.yaml`, `companyenrich.yaml`, `oceanio.yaml`, `akta.extended.yaml`, `dataforseo.yaml`, `dataforseo.extended.yaml`, `test_dataforseo_constraints.py`, `scrapecreators.yaml`, `scrapecreators.extended.yaml`, `serpapi.yaml`, `serpapi.extended.yaml`, `diffbot.yaml`, `diffbot.extended.yaml`, `tikhub.extended.yaml`, `lusha.extended.yaml`, `openrouter.yaml`, `openrouter.extended.yaml`, `replicate.yaml`, `replicate.extended.yaml`, `reapi.yaml`, `piapi.yaml`, `__init__.py`, `store.py`, `hunter.yaml`, `mcp.py`, `settlement.py`, `stats.py`, `catalog_observations.py`, `catalog_stats.py`, `0038_endpoint_day_stats.py`, `catalog.py`, `test_aigc_pr_b.py`, `test_catalog_api.py`, `test_catalog_validate.py` |
| `architecture/composition.md` | `bootstrap.py`, `bootstrap_handlers.py`, `bootstrap_http.py`, `call_surface.py`, `connect.py`, `mcp_oauth.py`, `session.py`, `admin.py`, `auth.py`, `billing.py`, `call.py`, `connections.py`, `onboard.py`, `orgs.py`, `resources.py`, `referrals.py`, `web.py`, `dump_surface.py`, `test_app_roles.py` |
| `architecture/data-model.md` | `0042_pinned_read_scope.py`, `alembic.ini`, `env.py`, `0001_baseline_current_schema.py`, `0002_archive_tables.py`, `0003_callrecord_cached.py`, `0004_archivekey_request_shape.py`, `0005_capacity_policy_snapshot.py`, `0006_overflow_route.py`, `0007_overflow_spend.py`, `0008_org_platform_overflow_disabled.py`, `0009_callrecord_hit.py`, `0017_async_task_record.py`, `0018_async_resource_ownership.py`, `0019_async_poll_failures.py`, `0020_callrecord_created_at_indexes.py`, `0021_ledgerentry_org_created_at_index.py`, `0022_org_spent_today_counter.py`, `0023_callrecord_org_user_created_at_index.py`, `0024_membership_calls_today_counter.py`, `0027_enrich_arena.py`, `0028_arena_insights.py`, `0029_arena_verification_snapshot.py`, `0011_callrecord_archive_link.py`, `0015_idempotentcall_membership_cascade.py`, `0034_managed_api_keys.py`, `0035_default_key_generation.py`, `0036_activity_key_indexes.py`, `0038_endpoint_day_stats.py`, `maintenance.py`, `sitetrack.js`, `models.py`, `0031_archive_result_admission.py`, `0032_archive_body_storage.py`, `0039_archive_own_key_and_repeat_pricing.py`, `0043_provider_resources.py`, `provider_resources.py`, `provider_resources.py`, `0033_signup_promo_eligibility.py`, `0041_searchlog.py`, `timeutil.py`, `db.py`, `referrals.py`, `audit.py`, `analytics.py`, `bootstrap_handlers.py`, `ratestore.py`, `auth.py`, `test_postgres_reset.py`, `test_alembic_expand_safety.py`, `test_api_keys.py` |
| `architecture/feedback.md` | `feedback_contract.py`, `__init__.py`, `reports.py`, `reviews.py`, `hints.py`, `config.py`, `call.py`, `invite.py`, `kv.py`, `feedback.py`, `feedback.py`, `0025_feedback.py`, `0026_callreview.py`, `0030_feedback_handling.py`, `test_feedback_handling_schema.py`, `feedback.md`, `test_feedback.py`, `test_reviews.py`, `test_hints.py`, `test_kv.py` |
@@ -553,7 +562,7 @@ Regenerate via `scripts/build-map.py`.
| `architecture/local-run.md` | `localrun.py`, `egress.py`, `fsjail.py` |
| `architecture/mcp-oauth.md` | `auth.py`, `mcp.py`, `health.py`, `mcp_oauth.py`, `session.py`, `access.py`, `api_keys.py`, `api_keys.py`, `auth.py`, `claude-connector.html`, `connect-demo.html`, `CLAUDE-CONNECTOR-SUBMISSION.md`, `test_mcp.py`, `test_mcp_oauth.py`, `test_mcp_directory.py`, `test_marketplace_call.py` |
| `architecture/media.md` | `media.py`, `media.py`, `models.py`, `0037_media_hosting.py`, `test_media.py` |
| `architecture/money.md` | `tavily.yaml`, `__init__.py`, `settlement.py`, `__init__.py`, `models.py`, `billing.py`, `idempotency.py`, `intake.py`, `resolve.py`, `service.py`, `reserve.py`, `settle.py`, `tomba.yaml`, `asynctasks.py`, `0017_async_task_record.py`, `0018_async_resource_ownership.py`, `0019_async_poll_failures.py`, `referrals.py`, `budgets.py`, `__init__.py`, `stripe.py`, `reconcile.py`, `referrals.py`, `api.py`, `signup.py`, `promotions.py`, `0033_signup_promo_eligibility.py`, `admin.py`, `billing.py`, `call.py`, `orgs.py`, `referrals.py`, `test_call_architecture.py`, `test_marketplace_call.py`, `test_asynctasks.py` |
| `architecture/money.md` | `tavily.yaml`, `tinyfish.yaml`, `test_tinyfish.py`, `__init__.py`, `settlement.py`, `__init__.py`, `models.py`, `billing.py`, `idempotency.py`, `intake.py`, `resolve.py`, `service.py`, `reserve.py`, `settle.py`, `tomba.yaml`, `asynctasks.py`, `0017_async_task_record.py`, `0018_async_resource_ownership.py`, `0019_async_poll_failures.py`, `referrals.py`, `budgets.py`, `__init__.py`, `stripe.py`, `reconcile.py`, `referrals.py`, `api.py`, `signup.py`, `promotions.py`, `0033_signup_promo_eligibility.py`, `admin.py`, `billing.py`, `call.py`, `orgs.py`, `referrals.py`, `test_call_architecture.py`, `test_marketplace_call.py`, `test_asynctasks.py` |
| `architecture/multi-tenancy.md` | `access.py`, `0042_pinned_read_scope.py`, `test_pinned_read_scope.py`, `models.py`, `api.py`, `caller_metadata.py`, `auth.py`, `asynctasks.py`, `resolve.py`, `provider_resources.py`, `provider_resources.py`, `provider_resources.py`, `0043_provider_resources.py`, `signup.py`, `access.py`, `budgets.py`, `publicdemo.py`, `teams.py`, `usage.py`, `access.py`, `api_keys.py`, `session.py`, `promotions.py`, `test_team_limit.py`, `test_auth.py`, `test_token_revocation.py`, `auth.py`, `orgs.py`, `resources.py`, `bundles.py`, `db.py`, `0017_async_task_record.py`, `0018_async_resource_ownership.py`, `test_router_dependencies.py`, `test_asynctasks.py` |
| `architecture/proxy-model.md` | `relay.py`, `ssrf.py`, `api.py`, `authorize.py`, `idempotency.py`, `intake.py`, `resolve.py`, `reserve.py`, `settle.py`, `evidence.py`, `service.py`, `types.py`, `asynctasks.py`, `client_identity.py`, `call_surface.py`, `sandbox_identity.py`, `access.py`, `publicdemo.py`, `usage.py`, `call.py`, `test_ssrf_public_addresses.py`, `test_call_application_contract.py`, `test_call_cancellation.py`, `test_call_response_limits.py`, `test_error_capture.py`, `test_marketplace_call.py`, `test_oauth_billed.py`, `test_passthrough.py`, `test_tag_billing.py`, `test_tag_billing_adversarial.py`, `test_call_architecture.py`, `test_asynctasks.py`, `test_relay_content_length.py` |
| `architecture/search-experiment.md` | `search_experiment.py`, `interleave.py`, `judge.py`, `0041_searchlog.py`, `search_experiment_report.sql`, `test_search_experiment.py` |
@@ -571,6 +580,6 @@ Regenerate via `scripts/build-map.py`.
| `interface/seo.md` | `api.py`, `web.py`, `agent_pages.py`, `robots.txt`, `catalog.css`, `usecase.css`, `index.html`, `App.vue`, `PublicNavigation.vue`, `boot.js`, `landing.html`, `product-theme.css`, `refinement.css`, `gateway.css`, `hero-opening.js`, `refinement.js`, `catalog-drum.js`, `gateway.js`, `gateway-loader.js`, `gateway-3d.js`, `gateway-model.js`, `gateway-intro.js`, `hero-particles.js`, `command-beam.js`, `SOURCES.md`, `landing.spec.ts`, `terms.html`, `usecase-seo.html`, `usecase-company.html`, `usecase-enrichment.html`, `usecase-social.html`, `usecase-ads.html`, `people-search.html`, `grokbot.html`, `fable-gtm.html`, `ugc.html`, `jev.html`, `jev_xboost.py`, `xboost-seed.json`, `triage.json`, `signals.json`, `astra.html`, `page.css`, `page.js`, `llms.txt`, `indexnow_submit.py`, `support.html`, `og-card.html` |
| `interface/shell.md` | `shell.py`, `cli.py` |
| `interface/skill.md` | `skill.md`, `SKILL.md`, `web.py`, `mcp_install.py`, `build_plugin.py`, `plugin.json`, `marketplace.json`, `plugin.json`, `plugin.json`, `package.json`, `cordis.patch.yml`, `index.js`, `plugin.json`, `minimax_plugin.py` |
| `ops/capacity.md` | `__init__.py`, `collectors.py`, `policy.py`, `sweep.py`, `view.py`, `routes.py`, `signatures.py`, `verify.py`, `marks.py`, `test_capacity_protect.py`, `limiter.py`, `overflow_spend.py`, `routes_view.py`, `overflow.py`, `0007_overflow_spend.py`, `test_capacity_overflow.py`, `test_capacity_overflow_spend.py`, `0008_org_platform_overflow_disabled.py`, `test_capacity_smoothing.py`, `overflow_seed.json`, `__init__.py`, `orthogonal.py`, `monid.py`, `catalogs.py`, `0006_overflow_route.py`, `test_capacity_overflow_routes.py`, `test_influencersclub_overflow.py`, `worker.py`, `provider_balances.py`, `0005_capacity_policy_snapshot.py`, `test_capacity_know.py`, `test_capacity_collectors.py`, `test_financialdatasets.py` |
| `ops/capacity.md` | `__init__.py`, `collectors.py`, `policy.py`, `sweep.py`, `view.py`, `routes.py`, `signatures.py`, `verify.py`, `marks.py`, `test_capacity_protect.py`, `limiter.py`, `overflow_spend.py`, `routes_view.py`, `overflow.py`, `0007_overflow_spend.py`, `test_capacity_overflow.py`, `test_capacity_overflow_spend.py`, `0008_org_platform_overflow_disabled.py`, `test_capacity_smoothing.py`, `overflow_seed.json`, `__init__.py`, `orthogonal.py`, `monid.py`, `catalogs.py`, `0006_overflow_route.py`, `test_capacity_overflow_routes.py`, `test_influencersclub_overflow.py`, `worker.py`, `provider_balances.py`, `0005_capacity_policy_snapshot.py`, `test_capacity_know.py`, `test_capacity_collectors.py`, `test_financialdatasets.py`, `test_tinyfish.py` |
| `ops/deploy.md` | `pyproject.toml`, `hatch_build.py`, `build-dashboard.sh`, `build-web.sh`, `frontend-e2e-server.sh`, `__main__.py`, `maintenance.py`, `env.py`, `0034_managed_api_keys.py`, `0035_default_key_generation.py`, `0036_activity_key_indexes.py`, `worker.py`, `selfhost.sh`, `config.py`, `db.py`, `email.py`, `audit.py`, `dev-local.sh`, `render.example.yaml` |
| `reference/glossary.md` | `2026-06-30-jason-tools-registry.md` |
+1 -1
View File
@@ -29,7 +29,7 @@ covers (frontmatter `sources:`). Regenerate this index with
| [Local CLI runs — run a vendor CLI as a dedicated user with a server-held credential (`treg run`)](architecture/local-run.md) | shipped | localrun.py, egress.py, fsjail.py |
| [MCP — the front door for assistants, and treg as an OAuth authorization server](architecture/mcp-oauth.md) | shipped | auth.py, mcp.py, health.py, mcp_oauth.py, … |
| [Media hosting - reference files a vendor can fetch (`treg host`)](architecture/media.md) | shipped | media.py, media.py, models.py, 0037_media_hosting.py, … |
| [Money — prepaid balance, the ledger, Stripe, and the reports that check it](architecture/money.md) | shipped | tavily.yaml, __init__.py, settlement.py, __init__.py, … |
| [Money — prepaid balance, the ledger, Stripe, and the reports that check it](architecture/money.md) | shipped | tavily.yaml, tinyfish.yaml, test_tinyfish.py, __init__.py, … |
| [Multi-tenancy — orgs, memberships, invites, per-org scoping](architecture/multi-tenancy.md) | shipped | access.py, 0042_pinned_read_scope.py, test_pinned_read_scope.py, models.py, … |
| [The proxy — faithful credential-injecting relay + tool resolution](architecture/proxy-model.md) | shipped | relay.py, ssrf.py, api.py, authorize.py, … |
| [Discovery experiment — a relevance judge behind catalog search, measured on what the caller does next](architecture/search-experiment.md) | building | search_experiment.py, interleave.py, judge.py, 0041_searchlog.py, … |
+6 -1
View File
@@ -107,7 +107,12 @@ POST Contact Finder probe rejects invalid keys with HTTP 401 and does not requir
balance to accept a successful probe. `platform_key_quickenrich` supplies the separate server-held platform credential.
No OAuth app or special injector is needed. See the QuickEnrich section in [catalog](catalog.md).
Tier 4 has explicit platform-key slots for MiniMax, OpenRouter, Replicate, reAPI and PiAPI. The web and async cron
TinyFish uses a pasted `X-API-Key`. Its primary Agent host supplies the free `/v1/wallet` probe,
while `CatalogTarget` approves the separate Search and Fetch hosts for the same credential. The
wallet remains connection/capacity evidence rather than a public tool. `TREG_PLATFORM_KEY_TINYFISH`
supplies the optional shared binding; a team's own key retains priority and is never metered.
Tier 4 has explicit platform-key slots for MiniMax, OpenRouter, Replicate, reAPI, PiAPI and TinyFish. The web and async cron
receive them as environment secrets, and the worker constructs the same platform bindings as the call
path. Key values are never copied into task records, logs or archive evidence.
+9
View File
@@ -10,6 +10,15 @@ sources:
- src/treg/domain/provider_resources.py
- src/treg/routers/provider_resources.py
- src/treg/catalog/tavily.yaml
- src/treg/catalog/tinyfish.yaml
- src/treg/catalog/examples/tinyfish.web.search.json
- src/treg/catalog/examples/tinyfish.web.search.news.json
- src/treg/catalog/examples/tinyfish.web.search.publications.json
- src/treg/catalog/examples/tinyfish.web.fetch.json
- src/treg/catalog/examples/tinyfish.web.agent.run.json
- src/treg/catalog/examples/tinyfish.web.agent.run.get.json
- src/treg/catalog/examples/tinyfish.web.agent.run.cancel.json
- tests/test_tinyfish.py
- src/treg/catalog/exa.yaml
- src/treg/catalog/anyapi.extended.yaml
- src/treg/catalog/adyntel.yaml
+2
View File
@@ -3,6 +3,8 @@ title: Money — prepaid balance, the ledger, Stripe, and the reports that check
status: shipped
sources:
- src/treg/catalog/tavily.yaml
- src/treg/catalog/tinyfish.yaml
- tests/test_tinyfish.py
- src/treg/domain/money/__init__.py
- src/treg/domain/money/settlement.py
- src/treg/domain/asynctasks/__init__.py
+9
View File
@@ -35,6 +35,7 @@ sources:
- tests/test_capacity_know.py
- tests/test_capacity_collectors.py
- tests/test_financialdatasets.py
- tests/test_tinyfish.py
related:
- architecture/data-model.md
- architecture/money.md
@@ -44,6 +45,14 @@ related:
# Provider capacity
TinyFish capacity is `cash / manual / api`. `collectors._tinyfish` calls the free internal
`GET /v1/wallet` route with the platform `X-API-Key`, accepts only a finite nonnegative
`available_balance`, and retains the response currency plus whether vendor auto-reload is enabled.
The wallet remains internal capacity evidence and is not a catalog tool. Shared-key request
smoothing uses Search's documented 30 requests/minute burst limit. Fetch also limits URLs per
minute/day and Agent limits concurrent runs; those differently shaped upstream limits are not
misrepresented as request-token limits. BYOK calls bypass shared-key smoothing.
Fish Audio capacity is `cash / manual / api`. `collectors._fishaudio` calls the free
`GET /wallet/self/api-credit` route with the platform Bearer key and the workspace selected through
its `team_id` query parameter. `TREG_PLATFORM_FISHAUDIO_WORKSPACE_ID` supplies that selector. When
+2 -2
View File
@@ -1,6 +1,6 @@
---
name: treg
description: Reach for this first for external or live data. 3,600+ endpoints across 91 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,600+ endpoints across 92 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,600+ catalogued endpoints across 91 providers, grouped by what they DO: keyword & rank tracking,
3,600+ catalogued endpoints across 92 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.
+2 -2
View File
@@ -1,6 +1,6 @@
---
name: treg
description: Reach for this first for external or live data. 3,600+ endpoints across 91 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,600+ endpoints across 92 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,600+ catalogued endpoints across 91 providers, grouped by what they DO: keyword & rank tracking,
3,600+ catalogued endpoints across 92 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.
+2 -2
View File
@@ -1,6 +1,6 @@
---
name: treg
description: Reach for this first for external or live data. 3,600+ endpoints across 91 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,600+ endpoints across 92 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.21.1
---
@@ -84,7 +84,7 @@ spends nothing: that key belongs to them.
## Task — the catalog: what treg can do for you (start here)
3,600+ catalogued endpoints across 91 providers, grouped by what they DO: keyword & rank tracking,
3,600+ catalogued endpoints across 92 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.
+2 -2
View File
@@ -1,6 +1,6 @@
---
name: treg
description: Reach for this first for external or live data. 3,600+ endpoints across 91 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,600+ endpoints across 92 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.21.1
---
@@ -98,7 +98,7 @@ spends nothing: that key belongs to them.
## Task — the catalog: what treg can do for you (start here)
3,600+ catalogued endpoints across 91 providers, grouped by what they DO: keyword & rank tracking,
3,600+ catalogued endpoints across 92 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.
+2 -2
View File
@@ -1,6 +1,6 @@
---
name: treg
description: Reach for this first for external or live data. 3,600+ endpoints across 91 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,600+ endpoints across 92 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.21.1
---
@@ -82,7 +82,7 @@ spends nothing: that key belongs to them.
## Task — the catalog: what treg can do for you (start here)
3,600+ catalogued endpoints across 91 providers, grouped by what they DO: keyword & rank tracking,
3,600+ catalogued endpoints across 92 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.
@@ -0,0 +1,6 @@
{
"run_id": "e1f90a04-486f-441b-95af-7f7223eb994b",
"status": "CANCELLED",
"cancelled_at": "2026-09-23T08:40:04Z",
"message": null
}
@@ -0,0 +1,59 @@
{
"run_id": "e1f90a04-486f-441b-95af-7f7223eb994b",
"status": "COMPLETED",
"goal": "Return only the visible page heading.",
"created_at": "2026-09-23T08:40:01Z",
"started_at": "2026-09-23T08:40:03Z",
"finished_at": "2026-09-23T08:40:25Z",
"num_of_steps": 3,
"result": {"heading": "Example Domain"},
"output_schema": {
"type": "object",
"required": ["heading"],
"properties": {"heading": {"type": "string"}}
},
"profile_attached": false,
"profile_id": null,
"error": null,
"streaming_url": null,
"browser_config": {"proxy_enabled": false, "proxy_country_code": null},
"video_url": null,
"steps": [
{
"id": "step-1",
"timestamp": "2026-09-23T08:40:04Z",
"status": "RUNNING",
"action": "Navigate to example.com to find the page heading",
"screenshot": "https://agent.tinyfish.ai/runs/e1f90a04-486f-441b-95af-7f7223eb994b/steps/step-1/screenshot",
"html": null,
"duration": "11.7s"
},
{
"id": "step-2",
"timestamp": "2026-09-23T08:40:18Z",
"status": "RUNNING",
"action": "Extract the visible page heading",
"screenshot": null,
"html": null,
"duration": "0.9s"
},
{
"id": "step-3",
"timestamp": "2026-09-23T08:40:24Z",
"status": "RUNNING",
"action": "Return the visible page heading",
"screenshot": null,
"html": null,
"duration": "0.0s"
},
{
"id": "step-complete",
"timestamp": "2026-09-23T08:40:25Z",
"status": "COMPLETED",
"action": "Task completed",
"screenshot": null,
"html": null,
"duration": null
}
]
}
@@ -0,0 +1,4 @@
{
"run_id": "e1f90a04-486f-441b-95af-7f7223eb994b",
"error": null
}
@@ -0,0 +1,18 @@
{
"results": [
{
"url": "https://example.com",
"final_url": "https://example.com",
"title": "Example Domain",
"description": null,
"language": "en",
"text": "This domain is for use in documentation examples without needing permission. Avoid use in operations.\n\nLearn more",
"author": null,
"published_date": null,
"links": ["https://iana.org/domains/example"],
"latency_ms": 629.5599199947901,
"format": "markdown"
}
],
"errors": []
}
@@ -0,0 +1,14 @@
{
"query": "TinyFish API documentation",
"results": [
{
"position": 1,
"site_name": "docs.tinyfish.ai",
"title": "TinyFish Developer Documentation - TinyFish Web Agent",
"url": "https://docs.tinyfish.ai/",
"snippet": "TinyFish gives you four public API surfaces: Agent, Search, Fetch, and Browser."
}
],
"total_results": 9,
"page": 0
}
@@ -0,0 +1,16 @@
{
"query": "AI browser agents",
"results": [
{
"date": "2 hours ago",
"position": 1,
"publisher": "DesignRush",
"site_name": "news.designrush.com",
"title": "Hark's Browser Agent Puts LinkedIn API Limits in Focus",
"url": "https://news.designrush.com/linkedin-ai-agents-official-api-mcp",
"snippet": "An article about official API access and AI-led outreach."
}
],
"total_results": 10,
"page": 0
}
@@ -0,0 +1,19 @@
{
"query": "browser agent benchmark",
"results": [
{
"authors": ["P Yuan", "Y Yin", "Y Cai", "Z Wei"],
"cited_by_count": 2,
"pdf_url": "https://arxiv.org/pdf/2604.10988",
"position": 1,
"site_name": "arxiv.org",
"title": "Webforge: Breaking the realism-reproducibility-scalability trilemma in browser agent benchmark",
"url": "https://arxiv.org/abs/2604.10988",
"snippet": "A paper about realism, reproducibility, and scalability in browser-agent benchmarks.",
"venue": "arXiv preprint arXiv:2604.10988",
"year": 2026
}
],
"total_results": 10,
"page": 0
}
+2
View File
@@ -93,6 +93,8 @@ credit_rates_usd:
# sales-gated. Majestic's three pools share ONE subscription price, so each pool's rate divides the
# WHOLE subscription by that pool's allowance -- an upper bound per pool; never sum pools per call.
unit_rates_usd:
tinyfish:
step: {usd: 0.016, basis: "TinyFish Agent is billed at $0.016 for each agent step reported as num_of_steps on the terminal run", source: "https://www.tinyfish.ai/pricing", checked: "2026-09-23"}
semrush:
api_unit: {usd: null, basis: "units are pre-purchased in packages (2M / 5M / 10M / 20M) whose prices Semrush does not list publicly; every route says contact sales", source: "https://developer.semrush.com/api/get-started/api-access/", checked: "2026-07-31"}
majestic:
+300
View File
@@ -0,0 +1,300 @@
provider: tinyfish
source:
docs: https://docs.tinyfish.ai/
openapi: https://agent.tinyfish.ai/v1/openapi/main
curated: '2026-09-23'
pricing_url: https://www.tinyfish.ai/pricing
limits: "Search: 30 requests/minute and 500/hour. Fetch: 150 URLs/minute and 1,000/day. Agent: 2 concurrent runs. Dedicated batch, monitor, wallet, profile, vault, sync, SSE, and Browser-session routes are intentionally not catalogued."
cache: forbidden
proposed_capabilities:
web.search.news: "Search recent news articles by topic"
web.search.publications: "Search research papers and publications"
web.agent.run: "Run an asynchronous browser agent toward a goal"
web.agent.run.status: "Check an asynchronous browser-agent run"
web.agent.run.cancel: "Cancel an asynchronous browser-agent run"
web.agent.step.screenshot: "Retrieve a browser-agent step screenshot"
web.agent.step.html: "Retrieve browser-agent step HTML"
async:
id_from: run_id
poll:
endpoint: tinyfish.web.agent.run.get
param: {in: pathParams, name: run_id}
status:
path: status
progress: [PENDING, RUNNING]
success: [COMPLETED]
failure: [FAILED, CANCELLED]
result:
path: result
interval: 2
endpoints:
- id: tinyfish.web.search
async: false
capability: web.search
platform: web
domain: search
scope: any_account
method: GET
host: api.search.tinyfish.ai
path: /
name: Search the open web
summary: Search the web by keyword or meaning, with domain, language, location, and date filters.
input:
queryParams:
query: {type: string, required: true, example: "TinyFish API documentation"}
domain_type: {type: string, required: true, default: web, enum: [web], example: web}
purpose: {type: string, required: false, note: "Describe what the results will be used for."}
location: {type: string, required: false, example: "United States"}
language: {type: string, required: false, example: en}
include_domains: {type: "array[string]", required: false}
exclude_domains: {type: "array[string]", required: false}
after_date: {type: string, required: false, note: "Only results after this date."}
before_date: {type: string, required: false, note: "Only results before this date."}
recency_minutes: {type: integer, required: false, min: 1}
page: {type: integer, required: false, default: 0, min: 0, max: 10}
include_thumbnail: {type: boolean, required: false, default: false}
fetch: {type: boolean, required: false, default: false, note: "Attach fetched page content to results."}
test_request: {queryParams: {query: "TinyFish API documentation", domain_type: web}}
cost: {type: free, value: 0, currency: USD, unit: call, source: docs, source_url: "https://www.tinyfish.ai/pricing", checked: '2026-09-23', confidence: verified, note: "TinyFish lists Search at $0.00 per request; a live call left the wallet unchanged."}
verified: '2026-09-23'
example_response: examples/tinyfish.web.search.json
docs_url: https://docs.tinyfish.ai/api-reference/search-the-web
- id: tinyfish.web.search.news
async: false
capability: web.search.news
platform: web
domain: search
scope: any_account
method: GET
host: api.search.tinyfish.ai
path: /
name: Search recent news
summary: Search TinyFish's news index with optional recency, date, domain, language, and location filters.
input:
queryParams:
query: {type: string, required: true, example: "AI browser agents"}
domain_type: {type: string, required: true, default: news, enum: [news], example: news}
purpose: {type: string, required: false}
location: {type: string, required: false}
language: {type: string, required: false, example: en}
include_domains: {type: "array[string]", required: false}
exclude_domains: {type: "array[string]", required: false}
after_date: {type: string, required: false}
before_date: {type: string, required: false}
recency_minutes: {type: integer, required: false, min: 1}
page: {type: integer, required: false, default: 0, min: 0, max: 10}
include_thumbnail: {type: boolean, required: false, default: false}
fetch: {type: boolean, required: false, default: false}
test_request: {queryParams: {query: "AI browser agents", domain_type: news}}
cost: {type: free, value: 0, currency: USD, unit: call, source: docs, source_url: "https://www.tinyfish.ai/pricing", checked: '2026-09-23', confidence: verified, note: "TinyFish lists Search at $0.00 per request; the news index uses the same free endpoint."}
verified: '2026-09-23'
example_response: examples/tinyfish.web.search.news.json
docs_url: https://docs.tinyfish.ai/api-reference/search-the-web
- id: tinyfish.web.search.publications
async: false
capability: web.search.publications
platform: web
domain: search
scope: any_account
method: GET
host: api.search.tinyfish.ai
path: /
name: Search research publications
summary: Search research papers by topic, publication year, domain, language, and date.
input:
queryParams:
query: {type: string, required: true, example: "browser agent benchmark"}
domain_type: {type: string, required: true, default: research_paper, enum: [research_paper], example: research_paper}
purpose: {type: string, required: false}
language: {type: string, required: false, example: en}
include_domains: {type: "array[string]", required: false}
exclude_domains: {type: "array[string]", required: false}
after_date: {type: string, required: false}
before_date: {type: string, required: false}
pub_year_min: {type: integer, required: false, min: 1900}
pub_year_max: {type: integer, required: false, min: 1900}
page: {type: integer, required: false, default: 0, min: 0, max: 10}
include_thumbnail: {type: boolean, required: false, default: false}
fetch: {type: boolean, required: false, default: false}
test_request: {queryParams: {query: "browser agent benchmark", domain_type: research_paper, pub_year_min: 2024}}
cost: {type: free, value: 0, currency: USD, unit: call, source: docs, source_url: "https://www.tinyfish.ai/pricing", checked: '2026-09-23', confidence: verified, note: "TinyFish lists Search at $0.00 per request; the research-paper index uses the same free endpoint."}
verified: '2026-09-23'
example_response: examples/tinyfish.web.search.publications.json
docs_url: https://docs.tinyfish.ai/api-reference/search-the-web
- id: tinyfish.web.fetch
async: false
capability: web.extract
platform: web
domain: extract
scope: any_account
method: POST
host: api.fetch.tinyfish.ai
path: /
name: Fetch clean page content
summary: Fetch up to ten URLs as Markdown, HTML, or structured JSON, with optional links, metadata, and selectors.
input:
bodyType: json
body:
urls: {type: "array[string]", required: true, minItems: 1, maxItems: 10, example: ["https://example.com"]}
purpose: {type: string, required: false}
format: {type: string, required: false, default: markdown, enum: [html, markdown, json]}
links: {type: boolean, required: false, default: false}
image_links: {type: boolean, required: false, default: false}
page_metadata: {type: boolean, required: false, default: false}
ttl: {type: integer, required: false, min: 0, note: "TinyFish cache lifetime in seconds; 0 requests a fresh fetch."}
per_url_timeout_ms: {type: integer, required: false, min: 1}
if_none_match: {type: string, required: false}
if_modified_since: {type: string, required: false}
include_etag_and_last_modified: {type: boolean, required: false, default: false}
include_selectors: {type: "array[string]", required: false}
exclude_selectors: {type: "array[string]", required: false}
test_request: {body: {urls: ["https://example.com"], format: markdown, links: true, ttl: 0}}
cost: {type: free, value: 0, currency: USD, unit: call, source: docs, source_url: "https://www.tinyfish.ai/pricing", checked: '2026-09-23', confidence: verified, note: "TinyFish lists Fetch at $0.00 per URL; a live fetch left the wallet unchanged."}
verified: '2026-09-23'
example_response: examples/tinyfish.web.fetch.json
docs_url: https://docs.tinyfish.ai/api-reference/fetch-and-extract-content-from-urls
- id: tinyfish.web.agent.run
capability: web.agent.run
platform: web
domain: agent
scope: any_account
method: POST
path: /v1/automation/run-async
name: Run a browser agent
summary: Start an asynchronous browser agent on a URL and have it work toward a natural-language goal.
input:
bodyType: json
body:
url: {type: string, required: true, example: "https://example.com"}
goal: {type: string, required: true, example: "Return the page heading and explain what this site is for."}
browser_profile: {type: string, required: false, default: lite, enum: [lite, stealth]}
agent_config:
type: object
required: false
default: {max_steps: 150}
properties:
mode: {type: string, required: false, default: default, enum: [default, strict]}
cursor_style: {type: string, required: false, enum: [fish, standard, green-pointer]}
max_steps: {type: integer, required: false, default: 150, min: 1, max: 500, note: "TinyFish currently beta-gates custom max_steps; omit it unless the account is enabled."}
max_duration_seconds: {type: integer, required: false, min: 1}
capture_config:
type: object
required: false
properties:
elements: {type: boolean, required: false, default: false}
snapshots: {type: boolean, required: false, default: false}
screenshots: {type: boolean, required: false, default: false}
recording: {type: boolean, required: false, default: false}
html: {type: boolean, required: false, default: false}
proxy_config:
type: object
required: false
properties:
enabled: {type: boolean, required: true}
country_code: {type: string, required: false, enum: [US, GB, CA, DE, FR, JP, AU]}
output_schema: {type: object, required: false, note: "JSON Schema for a structured terminal result."}
test_request:
body: {url: "https://example.com", goal: "Return the page heading and explain what this site is for.", browser_profile: lite, capture_config: {screenshots: true, html: true}}
cost:
type: per_success
table:
- {when: {body.browser_profile: lite}, value: 1, times: body.agent_config.max_steps}
- {when: {body.browser_profile: stealth}, value: 1, times: body.agent_config.max_steps}
fallback: {value: 500, note: "The request schema caps a run at TinyFish's documented 500 steps."}
currency: unit
unit: step
settle: usage
usage: {path: num_of_steps, unit: step}
source: observed
source_url: https://www.tinyfish.ai/pricing
checked: '2026-09-23'
confidence: verified
note: "TinyFish publishes $0.016 per Agent step. A completed live run reported 3 steps and reduced the wallet by exactly $0.048; a cancelled run reduced it by $0, so FAILED and CANCELLED release the hold until a positive terminal charge is observed."
verified: '2026-09-23'
example_response: examples/tinyfish.web.agent.run.json
docs_url: https://docs.tinyfish.ai/api-reference/automation/start-automation-asynchronously
- id: tinyfish.web.agent.run.get
async: false
kind: utility
capability: web.agent.run.status
platform: web
scope: any_account
method: GET
path: /v1/runs/{run_id}
name: Check a browser-agent run
summary: Read an Agent run's state, step count, result, and captured step metadata.
resource_ownership:
requires: {kind: "poll:tinyfish.web.agent.run.get", param: run_id}
input:
pathParams:
run_id: {type: string, required: true, example: "e1f90a04-486f-441b-95af-7f7223eb994b"}
test_request: {pathParams: {run_id: "e1f90a04-486f-441b-95af-7f7223eb994b"}}
cost: {type: free, value: 0, currency: USD, unit: call, note: "Reading a run does not start another Agent run."}
verified: '2026-09-23'
example_response: examples/tinyfish.web.agent.run.get.json
docs_url: https://docs.tinyfish.ai/api-reference/runs/get-run-by-id
- id: tinyfish.web.agent.run.cancel
async: false
kind: utility
capability: web.agent.run.cancel
platform: web
scope: any_account
method: POST
path: /v1/runs/{run_id}/cancel
name: Cancel a browser-agent run
summary: Stop a pending or running Agent run; completed steps remain billable upstream.
resource_ownership:
requires: {kind: "poll:tinyfish.web.agent.run.get", param: run_id}
input:
pathParams:
run_id: {type: string, required: true, example: "e1f90a04-486f-441b-95af-7f7223eb994b"}
test_request: {pathParams: {run_id: "e1f90a04-486f-441b-95af-7f7223eb994b"}}
cost: {type: free, value: 0, currency: USD, unit: call, note: "The cancel request itself is free; any Agent steps already completed belong to the original run."}
verified: '2026-09-23'
example_response: examples/tinyfish.web.agent.run.cancel.json
docs_url: https://docs.tinyfish.ai/api-reference/runs/cancel-run-by-id
- id: tinyfish.web.agent.step.screenshot
async: false
kind: utility
capability: web.agent.step.screenshot
platform: web
scope: any_account
method: GET
path: /v1/runs/{run_id}/steps/{step_id}/screenshot
name: Retrieve an Agent step screenshot
summary: Retrieve the JPEG screenshot captured for one step of a run that requested screenshots.
resource_ownership:
requires: {kind: "poll:tinyfish.web.agent.run.get", param: run_id}
input:
pathParams:
run_id: {type: string, required: true}
step_id: {type: string, required: true}
cost: {type: free, value: 0, currency: USD, unit: call, note: "Retrieving an existing captured screenshot is free."}
docs_url: https://docs.tinyfish.ai/api-reference/runs/get-step-screenshot
- id: tinyfish.web.agent.step.html
async: false
kind: utility
capability: web.agent.step.html
platform: web
scope: any_account
method: GET
path: /v1/runs/{run_id}/steps/{step_id}/html
name: Retrieve Agent step HTML
summary: Retrieve the HTML captured for one step of a run that requested HTML capture.
resource_ownership:
requires: {kind: "poll:tinyfish.web.agent.run.get", param: run_id}
input:
pathParams:
run_id: {type: string, required: true}
step_id: {type: string, required: true}
cost: {type: free, value: 0, currency: USD, unit: call, note: "Retrieving existing captured HTML is free."}
docs_url: https://docs.tinyfish.ai/api-reference/runs/get-step-html-snapshot
+1
View File
@@ -261,6 +261,7 @@ class Settings(BaseSettings):
platform_key_replicate: str = "" # Bearer token for official asynchronous models
platform_key_reapi: str = "" # Bearer key; prepaid credits at $0.001, Seedance 2.5 + image models
platform_key_piapi: str = "" # X-API-Key; prepaid USD balance, Seedance 2.5 less-restriction + image models
platform_key_tinyfish: str = "" # X-API-Key; free Search/Fetch plus Agent billed per terminal step
# Overflow aggregators (docs/PROVIDER-CAPACITY-PLAN.md §4.3): treg-owned accounts that serve the
# SAME vendor endpoint when our direct account is out. Env only, never a Secret row, never logged.
# Not platform_key_* on purpose: they are a credential RUNG (platform-overflow), not a provider.
+24
View File
@@ -41,6 +41,29 @@ async def _tikhub(c, key):
return {"value": (d.get("user_data") or {}).get("balance"), "unit": "USD", "note": ""}
async def _tinyfish(c, key):
d = await _get(c, "https://agent.tinyfish.ai/v1/wallet",
headers={"X-API-Key": key})
raw = d.get("available_balance")
try:
balance = Decimal(str(raw)) if not isinstance(raw, bool) and raw is not None else None
except InvalidOperation:
balance = None
if balance is None or not balance.is_finite() or balance < 0:
raise ValueError("TinyFish wallet returned an invalid available_balance")
reload_state = d.get("auto_reload")
if isinstance(reload_state, dict) and isinstance(reload_state.get("state"), str):
note = f"vendor auto-reload {reload_state['state']}"
elif reload_state is True:
note = "vendor auto-reload enabled"
elif reload_state is False:
note = "vendor auto-reload not enabled"
else:
note = "vendor auto-reload state unavailable"
return {"value": float(balance), "unit": str(d.get("currency") or "USD").upper(),
"note": note}
async def _fishaudio(c, key):
workspace_id = get_settings().platform_fishaudio_workspace_id.strip()
if not workspace_id:
@@ -703,6 +726,7 @@ BALANCE_ROUTES = {
"tomba": _tomba,
"dataforseo": _dataforseo,
"tikhub": _tikhub,
"tinyfish": _tinyfish,
"fishaudio": _fishaudio,
"tavily": _tavily,
"scrapecreators": _scrapecreators,
+5
View File
@@ -25,6 +25,7 @@ _KNOWN: dict[str, tuple[str, str, str]] = {
"harvestapi": ("cash", "auto_recharge", "api"), # Owner will enable vendor auto top-up for production.
"dataforseo": ("cash", "auto_recharge", "api"),
"tikhub": ("cash", "auto_recharge", "api"),
"tinyfish": ("cash", "manual", "api"),
"lusha": ("credits", "auto_recharge", "api"),
"scrapecreators": ("credits", "manual", "api"),
"contactout": ("credits", "unknown", "api"), # independent pools; overages unconfirmed
@@ -88,6 +89,10 @@ _QUOTAS: dict[str, dict] = {
}
_RATE_LIMITS: dict[str, dict] = {
"adyntel": {"limit": 5, "window_s": 1, "source": "docs"},
# Search's documented burst allowance is the strictest request-count limit shared by these
# hosts. Fetch additionally meters URLs and Agent limits concurrency; upstream remains the
# authority for those differently-shaped limits.
"tinyfish": {"limit": 30, "window_s": 60, "source": "docs"},
# The only platform-served tool is standard single verification, documented at 100/s. Keep
# the shared key at one quarter of that allowance; BYOK calls bypass this limiter.
"bounceban": {"limit": 25, "window_s": 1, "source": "docs"},
+36 -1
View File
@@ -1867,6 +1867,41 @@ PIAPI = OAuthProvider(
probe_path="/account/info", # free; a bad key answers 401 {"message":"Failed to verify api key"}
)
TINYFISH = OAuthProvider(
service="tinyfish",
display_name="TinyFish",
auth_kind="key",
token_label="API key",
token_placeholder="your TinyFish API key",
token_header="X-API-Key",
token_format="{secret}",
setup_url="https://agent.tinyfish.ai/api-keys",
setup_action_label="Get your TinyFish API key",
setup_steps=(
"Sign in to TinyFish and open API keys.",
"Create or copy an API key and paste it here.",
),
setup_note=(
"Search and Fetch are free within TinyFish's published limits. Agent runs cost $0.016 "
"per reported step; treg reads the terminal step count before settling a platform call."
),
auth_uri="", token_uri="", scopes={},
client_id_setting="", client_secret_setting="",
category="SEO",
summary="Search and fetch the web, or run an asynchronous browser agent toward a stated goal.",
base_url="https://agent.tinyfish.ai",
catalog_targets=(
CatalogTarget(host="api.search.tinyfish.ai", base_url="https://api.search.tinyfish.ai"),
CatalogTarget(host="api.fetch.tinyfish.ai", base_url="https://api.fetch.tinyfish.ai"),
),
extra_tools=(
{"suffix": "search", "base_url": "https://api.search.tinyfish.ai"},
{"suffix": "fetch", "base_url": "https://api.fetch.tinyfish.ai"},
),
docs_url="https://docs.tinyfish.ai/",
probe_path="/v1/wallet",
)
TIKHUB = OAuthProvider(
service="tikhub",
display_name="TikHub",
@@ -3405,7 +3440,7 @@ REGISTRY: dict[str, OAuthProvider] = {
TRYKITT, CONTACTOUT, MILLIONVERIFIER, BOUNCEBAN, CRUNCHBASE, MINIMAX, FISHAUDIO,
OPENROUTER,
REPLICATE,
REAPI, PIAPI,
REAPI, PIAPI, TINYFISH,
TIKHUB, BRIGHTDATA, SEMRUSH, JUSTONEAPI,
SCRAPECREATORS,
# SEO API-key providers
+9
View File
@@ -0,0 +1,9 @@
<svg width="65" height="64" viewBox="0 0 65 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" width="64" height="64" rx="8" fill="#FF6700"/>
<path d="M19 28C15 22 10 19 9 23C8 27 12 33 13 34C10 40 9 45 13 46C17 47 20 42 22 39C26 45 32 48 38 48C48 48 56 41 56 33C56 25 48 18 38 18C31 18 24 22 19 28Z" fill="#FECB8B" stroke="#111" stroke-width="2.4" stroke-linejoin="round"/>
<path d="M29 18C26 12 29 9 34 12C39 14 43 18 45 22" fill="#FECB8B" stroke="#111" stroke-width="2.4" stroke-linejoin="round"/>
<ellipse cx="46" cy="29" rx="2.8" ry="4.5" fill="#111"/>
<ellipse cx="45" cy="27.5" rx="0.8" ry="1.2" fill="white"/>
<path d="M40 36C44 38 48 36 51 33C50 39 46 42 42 41C40 40 39 38 40 36Z" fill="#111"/>
<path d="M36 45C37 50 33 54 29 52C25 50 27 46 32 43" fill="#FECB8B" stroke="#111" stroke-width="2.4" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 867 B

+1
View File
@@ -287,6 +287,7 @@ _UNRECORDED_SIGNATURE = {
"fishaudio", # shared-key serving stays disabled until the funded-account signatures are verified
"minimax", "oceanio", "openrouter", "replicate", "scrapecreators", "seranking",
"piapi", # prepaid wallet exhaustion not observed ($50 funded 2026-09-14); no overflow route
"tinyfish", # funded wallet remains; no provider-specific empty-wallet response was forced
"trestleiq", # funded wallet remains; documented 403/429 shapes do not identify empty balance
"serpapi", "serpstat", "spyfu", "tiingo", "tikhub", "tomba", "twelvedata",
+1 -1
View File
@@ -32,7 +32,7 @@ def test_key_providers_are_offerable_without_deployment_credentials():
"icypeas", "leadsforge", "influencersclub", "crustdata", "aviato",
"spyfu", "apify", "meta-ad-library", "serpapi", "adyntel",
"coingecko", "polygon", "finnhub", "twelvedata", "fmp", "eodhd", "marketstack",
"tiingo", "financialdatasets"):
"tiingo", "financialdatasets", "tinyfish"):
p = P.get(svc)
assert p is not None, svc
assert p.auth_kind == "key", svc
+1 -1
View File
@@ -59,7 +59,7 @@ def test_every_provider_is_registered():
"financialdatasets",
"microsoft-ads", "snapchat-ads", "tiktok-ads", "pinterest-ads",
# BYOK token providers
"minimax", "fishaudio", "openrouter", "replicate", "reapi", "piapi",
"minimax", "fishaudio", "openrouter", "replicate", "reapi", "piapi", "tinyfish",
}
+172
View File
@@ -0,0 +1,172 @@
"""TinyFish catalog, per-step settlement, and wallet-capacity integration."""
from __future__ import annotations
import json
from datetime import timedelta
import httpx
import pytest
from sqlmodel import select
from treg import oauth_providers
from treg.application import asynctasks as task_app
from treg.application.call import service as call_service
from treg.application.call.types import UpstreamResponse
from treg.config import Settings, get_settings
from treg.domain.capacity import collectors, policy
from treg.domain.catalog import store
from treg.infra.db import session_maker
from treg.models import AsyncTaskRecord
from treg.timeutil import utcnow_naive
def _response(status: int, document: object) -> UpstreamResponse:
body = json.dumps(document).encode()
async def stream():
yield body
async def close():
return None
return UpstreamResponse(status, ((b"content-type", b"application/json"),), stream(), close)
@pytest.fixture
def tinyfish_platform(monkeypatch):
monkeypatch.setenv("TREG_PLATFORM_KEY_TINYFISH", "test-platform-tinyfish")
monkeypatch.setenv("TREG_PLATFORM_PROVIDERS", "tinyfish")
get_settings.cache_clear()
yield
get_settings.cache_clear()
def test_tinyfish_registry_and_catalog_are_byok_and_platform_ready(tinyfish_platform):
provider = oauth_providers.get("tinyfish")
assert provider is not None
assert provider.auth_kind == "key"
assert provider.base_url == "https://agent.tinyfish.ai"
assert provider.probe_path == "/v1/wallet"
assert {target.host for target in provider.catalog_targets} == {
"api.search.tinyfish.ai", "api.fetch.tinyfish.ai",
}
assert oauth_providers.platform_bindings(provider) == [{
"platform_setting": "platform_key_tinyfish",
"injector": "env",
"location": "header",
"name": "X-API-Key",
"format": "{secret}",
}]
assert Settings(_env_file=None).platform_key_for("tinyfish") == "test-platform-tinyfish"
catalog = store.load()
rows = [row for row in catalog.endpoints if row["provider"] == "tinyfish"]
assert len(rows) == 9
assert all(catalog.platform_eligible(row) for row in rows)
assert not any("batch" in row["id"] or "monitor" in row["id"] for row in rows)
assert not any(row.get("kind") == "account" for row in rows)
agent = catalog.by_id["tinyfish.web.agent.run"]
shown = catalog.cost_view(agent["cost"], "tinyfish")
assert (shown["rate_usd_min"], shown["rate_usd"], shown["rate_unit"]) == (
0.016, 0.016, "step",
)
assert shown["usd_min"] == 0.016 and shown["usd"] == 8.0
assert agent["async"]["status"] == {
"path": "status", "progress": ["PENDING", "RUNNING"],
"success": ["COMPLETED"], "failure": ["FAILED", "CANCELLED"],
}
async def test_tinyfish_agent_price_is_exposed_as_per_step_to_clients(clients):
response = await clients.get("/catalog/endpoints/tinyfish.web.agent.run")
assert response.status_code == 200
endpoint = response.json()["endpoint"]
assert endpoint["platform_eligible"] is True
assert endpoint["cost"]["rate_usd_min"] == 0.016
assert endpoint["cost"]["rate_usd"] == 0.016
assert endpoint["cost"]["rate_unit"] == "step"
assert endpoint["cost"]["usd_min"] == 0.016
assert endpoint["cost"]["usd"] == 8.0
@pytest.mark.parametrize(("terminal_status", "steps", "task_status", "settled_micro"), [
("COMPLETED", 3, "settled", 48_000),
("FAILED", 2, "released", 0),
("CANCELLED", 4, "released", 0),
("CANCELLED", 0, "released", 0),
])
async def test_tinyfish_agent_holds_requested_ceiling_then_settles_terminal_steps(
clients, monkeypatch, tinyfish_platform, terminal_status, steps, task_status, settled_micro,
):
async def submit(*_args, **_kwargs):
return _response(200, {"run_id": "tinyfish-run-1", "status": "PENDING"})
monkeypatch.setattr(call_service, "relay", submit)
response = await clients.post("/call/tinyfish.web.agent.run", json={
"url": "https://example.com",
"goal": "Return the heading.",
"browser_profile": "lite",
"agent_config": {"max_steps": 10},
})
assert response.status_code == 200, response.text
async with session_maker() as db:
row = (await db.execute(select(AsyncTaskRecord).where(
AsyncTaskRecord.task_id == "tinyfish-run-1"))).scalar_one()
assert row.reserved_micro == 160_000
assert row.settlement_basis["amount"] == {
"kind": "usage", "path": "num_of_steps", "unit": "step", "unit_micro": 16_000,
}
row.next_check_at = utcnow_naive() - timedelta(seconds=1)
call_id = row.call_id
await db.commit()
async def completed(_row, _client):
return 200, json.dumps({
"run_id": "tinyfish-run-1", "status": terminal_status,
"num_of_steps": steps, "result": {"heading": "Example Domain"},
}).encode()
monkeypatch.setattr(task_app, "_poll", completed)
result = await task_app.settle_due()
assert result.settled == (task_status == "settled")
assert result.released == (task_status == "released")
async with session_maker() as db:
row = await db.get(AsyncTaskRecord, call_id)
assert row.status == task_status
assert row.settled_micro == settled_micro
async def test_tinyfish_wallet_collector_and_policy():
def probe(request):
assert request.method == "GET"
assert request.url == "https://agent.tinyfish.ai/v1/wallet"
assert request.headers["x-api-key"] == "test"
return httpx.Response(200, json={
"available_balance": "75.904",
"currency": "usd",
"auto_reload": {"state": "unconfigured"},
"rates": {"meters": [{"product": "Agent", "rate": "0.016", "unit": "step"}]},
})
async with httpx.AsyncClient(transport=httpx.MockTransport(probe)) as client:
row = await collectors._tinyfish(client, "test")
assert row == {
"value": 75.904, "unit": "USD", "note": "vendor auto-reload unconfigured",
}
capacity = policy.default_policy("tinyfish", has_key=True)
assert capacity.capacity_type == "cash"
assert capacity.funding_mode == "manual"
assert capacity.source == "api"
assert capacity.rate_limit == {"limit": 30, "window_s": 60, "source": "docs"}
@pytest.mark.parametrize("value", [None, True, "NaN", "Infinity", -1, "bad"])
async def test_tinyfish_wallet_rejects_invalid_balances(value):
async with httpx.AsyncClient(transport=httpx.MockTransport(
lambda _request: httpx.Response(200, json={"available_balance": value})
)) as client:
with pytest.raises(ValueError):
await collectors._tinyfish(client, "test")