unique-search-proxy 2026.44.0.dev10__tar.gz → 2026.44.0.dev11__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/PKG-INFO +1 -1
  2. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/pyproject.toml +1 -1
  3. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/api/v1/crawl.py +20 -0
  4. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/request_limits.py +20 -1
  5. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/README.md +0 -0
  6. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/__init__.py +0 -0
  7. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/__init__.py +0 -0
  8. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/api/__init__.py +0 -0
  9. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/api/health.py +0 -0
  10. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/api/v1/__init__.py +0 -0
  11. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/api/v1/agent_search.py +0 -0
  12. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/api/v1/configuration.py +0 -0
  13. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/api/v1/context_headers.py +0 -0
  14. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/api/v1/openapi_examples.py +0 -0
  15. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/api/v1/search.py +0 -0
  16. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/app.py +0 -0
  17. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/context.py +0 -0
  18. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/__init__.py +0 -0
  19. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/agent_engines/__init__.py +0 -0
  20. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/agent_engines/bing/cleanup.py +0 -0
  21. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/agent_engines/bing/client.py +0 -0
  22. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/agent_engines/bing/runner.py +0 -0
  23. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/agent_engines/bing/service.py +0 -0
  24. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/agent_engines/factory.py +0 -0
  25. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/agent_engines/service_base.py +0 -0
  26. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/agent_engines/structured_output.py +0 -0
  27. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/agent_engines/vertexai/client.py +0 -0
  28. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/agent_engines/vertexai/gemini.py +0 -0
  29. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/agent_engines/vertexai/redirects.py +0 -0
  30. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/agent_engines/vertexai/service.py +0 -0
  31. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/client/__init__.py +0 -0
  32. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/client/azure_transport.py +0 -0
  33. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/client/credentials.py +0 -0
  34. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/client/service.py +0 -0
  35. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/__init__.py +0 -0
  36. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/basic/__init__.py +0 -0
  37. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/basic/processing/__init__.py +0 -0
  38. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/basic/processing/errors.py +0 -0
  39. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/basic/processing/html_markdown.py +0 -0
  40. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/basic/processing/processors/__init__.py +0 -0
  41. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/basic/processing/processors/html.py +0 -0
  42. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/basic/processing/processors/pdf.py +0 -0
  43. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/basic/processing/processors/plain_text.py +0 -0
  44. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/basic/processing/registry.py +0 -0
  45. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/basic/service.py +0 -0
  46. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/basic/settings.py +0 -0
  47. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/basic/user_agent.py +0 -0
  48. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/factory.py +0 -0
  49. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/firecrawl/polling.py +0 -0
  50. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/firecrawl/request_body.py +0 -0
  51. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/firecrawl/service.py +0 -0
  52. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/jina/request_body.py +0 -0
  53. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/jina/service.py +0 -0
  54. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/pinned_egress.py +0 -0
  55. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/tavily/request_body.py +0 -0
  56. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/tavily/service.py +0 -0
  57. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/provider_response.py +0 -0
  58. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/providers.py +0 -0
  59. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/registry.py +0 -0
  60. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/__init__.py +0 -0
  61. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/brave/__init__.py +0 -0
  62. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/brave/pagination.py +0 -0
  63. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/brave/query_params.py +0 -0
  64. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/brave/service.py +0 -0
  65. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/custom_api/__init__.py +0 -0
  66. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/custom_api/service.py +0 -0
  67. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/descriptor.py +0 -0
  68. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/factory.py +0 -0
  69. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/google/__init__.py +0 -0
  70. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/google/pagination.py +0 -0
  71. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/google/query_params.py +0 -0
  72. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/google/service.py +0 -0
  73. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/pagination.py +0 -0
  74. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/perplexity/__init__.py +0 -0
  75. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/perplexity/request_body.py +0 -0
  76. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/perplexity/service.py +0 -0
  77. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/service_base.py +0 -0
  78. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/url_safety/__init__.py +0 -0
  79. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/url_safety/gate.py +0 -0
  80. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/error_handlers.py +0 -0
  81. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/helm/__init__.py +0 -0
  82. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/helm/generator/__init__.py +0 -0
  83. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/helm/generator/introspect.py +0 -0
  84. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/helm/generator/schema.py +0 -0
  85. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/helm/generator/template.py +0 -0
  86. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/helm/generator/templates/provider_values_yaml.j2 +0 -0
  87. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/helm/generator/values_yaml.py +0 -0
  88. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/helm/metadata.py +0 -0
  89. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/helm/registry.py +0 -0
  90. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/helm/url_safety_registration.py +0 -0
  91. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/logging_config.py +0 -0
  92. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/middleware/context.py +0 -0
  93. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/monitoring/__init__.py +0 -0
  94. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/monitoring/metrics.py +0 -0
  95. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/monitoring/setup.py +0 -0
  96. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/presets/__init__.py +0 -0
  97. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/presets/common.py +0 -0
  98. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/presets/crawl.py +0 -0
  99. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/presets/search.py +0 -0
  100. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/presets/types.py +0 -0
  101. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/__init__.py +0 -0
  102. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/app.py +0 -0
  103. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/base.py +0 -0
  104. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/client.py +0 -0
  105. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/monitoring.py +0 -0
  106. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/providers/__init__.py +0 -0
  107. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/providers/base.py +0 -0
  108. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/providers/bing_agent.py +0 -0
  109. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/providers/brave.py +0 -0
  110. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/providers/firecrawl.py +0 -0
  111. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/providers/google.py +0 -0
  112. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/providers/jina.py +0 -0
  113. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/providers/perplexity.py +0 -0
  114. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/providers/tavily.py +0 -0
  115. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/providers/vertexai_agent.py +0 -0
  116. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/secret_str.py +0 -0
  117. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/startup_report.py +0 -0
  118. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/utils/__init__.py +0 -0
  119. {unique_search_proxy-2026.44.0.dev10 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/utils/url.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: unique-search-proxy
3
- Version: 2026.44.0.dev10
3
+ Version: 2026.44.0.dev11
4
4
  Summary: Web Search Proxy implementation
5
5
  Author: ThePhilAz
6
6
  Author-email: ThePhilAz <rami.azouz@philico.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "unique-search-proxy"
3
- version = "2026.44.0.dev10"
3
+ version = "2026.44.0.dev11"
4
4
  description = "Web Search Proxy implementation"
5
5
  authors = [{ name = "ThePhilAz", email = "rami.azouz@philico.com" }]
6
6
  readme = "README.md"
@@ -9,6 +9,7 @@ from unique_search_proxy_core.crawlers.config_types import CrawlRequest
9
9
  from unique_search_proxy_core.errors import (
10
10
  ProxyError,
11
11
  UpstreamTimeoutError,
12
+ ValidationProxyError,
12
13
  attach_request_context,
13
14
  )
14
15
  from unique_search_proxy_core.schema import (
@@ -35,6 +36,9 @@ from unique_search_proxy_client.web.monitoring.metrics import (
35
36
  record_crawl_success,
36
37
  record_crawl_url_outcomes,
37
38
  )
39
+ from unique_search_proxy_client.web.settings.request_limits import (
40
+ request_limits_settings,
41
+ )
38
42
 
39
43
  router = APIRouter(tags=["crawl"])
40
44
  _LOGGER = logging.getLogger(__name__)
@@ -48,6 +52,21 @@ def _crawl_request_context(exc: ProxyError, *, crawler_id: str) -> ProxyError:
48
52
  )
49
53
 
50
54
 
55
+ def _enforce_url_limits(urls: list[str]) -> None:
56
+ limits = request_limits_settings
57
+ if len(urls) > limits.max_urls_per_crawl:
58
+ raise ValidationProxyError(
59
+ f"Crawl request lists {len(urls)} URLs; "
60
+ f"at most {limits.max_urls_per_crawl} are allowed",
61
+ )
62
+ for index, url in enumerate(urls):
63
+ if len(url) > limits.max_url_length:
64
+ raise ValidationProxyError(
65
+ f"URL at index {index} is {len(url)} characters long; "
66
+ f"at most {limits.max_url_length} are allowed",
67
+ )
68
+
69
+
51
70
  def _url_outcomes(results: list[CrawlUrlResult]) -> list[tuple[str, str, str]]:
52
71
  """Derive ``(outcome, error_code, http_status)`` triples from merged results."""
53
72
  outcomes: list[tuple[str, str, str]] = []
@@ -84,6 +103,7 @@ async def crawl(
84
103
  )
85
104
 
86
105
  try:
106
+ _enforce_url_limits(body.urls)
87
107
  async with asyncio.timeout(timeout):
88
108
  registry = get_http_client_registry(request.app)
89
109
  client = await registry.client_for(get_request_context())
@@ -17,7 +17,7 @@ REQUEST_LIMITS_ENV_PREFIX = "REQUEST_LIMITS_"
17
17
  egress=None,
18
18
  env_prefix=REQUEST_LIMITS_ENV_PREFIX,
19
19
  sections={
20
- "crawl": ["max_crawl_response_kib"],
20
+ "crawl": ["max_crawl_response_kib", "max_urls_per_crawl", "max_url_length"],
21
21
  },
22
22
  )
23
23
  class RequestLimitsSettings(BaseSettings):
@@ -41,6 +41,25 @@ class RequestLimitsSettings(BaseSettings):
41
41
  ),
42
42
  )
43
43
 
44
+ # Matches the search fetch_size ceiling: the web search tool crawls a whole
45
+ # result page in one request.
46
+ max_urls_per_crawl: int = Field(
47
+ default=100,
48
+ ge=1,
49
+ description=(
50
+ "Maximum number of URLs in one crawl request. Requests with more "
51
+ "are rejected with a 422."
52
+ ),
53
+ )
54
+ max_url_length: int = Field(
55
+ default=2048,
56
+ ge=1,
57
+ description=(
58
+ "Maximum length of each URL, in characters. Requests containing a "
59
+ "longer URL are rejected with a 422."
60
+ ),
61
+ )
62
+
44
63
  @property
45
64
  def max_crawl_response_bytes(self) -> int:
46
65
  return self.max_crawl_response_kib * 1024