How to Add Web Data to an Amazon Bedrock Knowledge Base
A Bedrock knowledge base is AWS's retrieval-augmented generation (RAG) feature. It indexes documents and returns relevant chunks. Since September 30, 2026, only managed knowledge bases accept new Web Crawler connectors. AWS policy restricts that crawler to your own or authorized sites. This guide compares both AWS crawlers and tests 2 ways to add external web data with Decodo.
Justinas Tamasevicius
Last updated: Nov 10, 2026
20 min read

TL;DR
- Since September 30, 2026, customer-managed Bedrock knowledge bases can't add new Web Crawler connectors, and AWS says existing connectors keep working.
- For sites you own or are authorized to crawl, use the managed Web Crawler with its crawl scope and link limit (syncScope, maxLinksPerUrl) set explicitly. Tuned this way, it matched the Decodo pipeline's retrieval results in testing.
- For public sites that allow crawling, one Decodo /crawl job converts a site section to Markdown for S3 or a custom data source.
- Exclude duplicate pages such as print views, which outranked the correct pages in our Kubernetes docs test.
What is a Bedrock knowledge base?
A Bedrock knowledge base connects a model in Amazon Bedrock to your own data, such as documents and web pages. It runs the retrieval step of retrieval-augmented generation, so AWS Bedrock RAG applications can answer from your data. When an app or agent asks a question, the knowledge base returns the most relevant chunks.
In a managed knowledge base, AWS manages storage, indexing, and retrieval. The managed type has been generally available since June 17, 2026. In a customer-managed knowledge base, which the console calls an unstructured vector store KB, you choose and manage the vector store.
A standalone vector database stores and searches embeddings. A knowledge base adds ingestion, chunking, embedding, and the Retrieve API.
What changed for the Bedrock knowledge base web crawler on September 30, 2026
Starting September 30, 2026, you can't create new Web Crawler, Confluence, Microsoft SharePoint, or Salesforce connectors on customer-managed knowledge bases. The AWS notice on the crawler page says that existing connectors "will continue to function, including data ingestion and retrieval".

To add a website to a customer-managed knowledge base, such as one on OpenSearch Serverless, load its pages through S3 or a custom data source.
Managed knowledge bases accept new Web Crawler connectors, and their crawler renders JavaScript.
The table below compares the Amazon Bedrock Web Crawler on a customer-managed knowledge base and on a managed one, covering availability, crawl limits, JavaScript rendering, authentication, and access control:
Aspect
Web Crawler on a customer-managed knowledge base
Web Crawler on a managed knowledge base
New connectors
Not available since September 30, 2026. Existing ones keep working
Available
Release status
Preview
Part of the managed knowledge base (GA June 17, 2026)
Starting points
Up to 10 seed URLs
Up to 10 seed URLs or up to 3 sitemap URLs
Crawl limits
1 to 25K pages per sync. If the crawl finds more, the sync fails and ingests nothing
The docs don't state a page cap
Depth and links per URL
Not listed
Depth 0 to 10, and up to 1K links per URL (default 100)
JavaScript
Static websites only
Renders JavaScript, without simulating user interactions such as clicks or scrolling
Authentication
None documented
Basic, form-based, or SAML authentication
Crawl rate
1 to 300 URLs per host per minute
1 to 300 URLs per minute (AWS doesn't say per host)
Followed (RFC 9309). AWS says "disallow by default if robots.txt is not found"
Followed (RFC 9309), including crawl-delay
Document-level access control
Not supported. Anyone with bedrock:Retrieve can read synced pages
Not supported. Anyone with access to the knowledge base can read indexed pages
Vector storage
Amazon OpenSearch Serverless only
AWS-managed storage
Managed or customer-managed: Which Bedrock knowledge base to build
For a new project that needs a website as a source, build a Bedrock managed knowledge base. It accepts new Web Crawler connectors, and AWS recommends it for retrieval accuracy.
The managed knowledge base has native connectors for Amazon S3, the Web Crawler, custom data sources, and apps such as SharePoint, Confluence, and Salesforce. AWS manages parsing, embeddings, and reranking. Agentic retrieval (multi-step search) is an option, priced per call. Default quotas allow 200 data sources and 10 TB of raw data per knowledge base, and both can be raised.
Pick a customer-managed knowledge base when you need a specific vector store or full control of parsing and chunking.
Choose the route that fits your situation:
Your situation
What to do
New knowledge base, and your own site or one you're authorized to crawl
Build a managed knowledge base with the Web Crawler, and set its scope and link limit as tested below
Existing customer-managed knowledge base with a Web Crawler data source
Keep it. AWS says existing connectors keep ingesting and serving retrieval
Existing customer-managed knowledge base and a new website
Load the pages through S3 or a custom data source, since new Web Crawler connectors aren't available there
Public site you don't own, whose terms and robots.txt allow crawling
Crawl it with Decodo's /crawl and load the pages through S3 or a custom data source. Treat the pages as untrusted input
For setup steps, follow AWS's guide to build a managed knowledge base.
Where the built-in web crawler stops
The Bedrock Web Crawler works best on sites you own. For external, bot-protected, or interactive sites, check these points:
- Authorization. AWS's crawler docs say to use the Web Crawler only on pages that you own or are authorized to crawl. The Amazon Acceptable Use Policy also applies. The connector usually doesn't fit competitor pages, industry news, or public review pages. With any crawler, first check whether a site allows scraping and read about the legal side of collecting public web data.
- Seed URL limit. Each data source takes up to 10 seed URLs or 3 sitemap URLs. A knowledge base with many sites needs one data source for every 10 seed URLs.
- User interactions. The managed crawler renders JavaScript without clicking, scrolling, or opening menus. On many pages that load content with JavaScript, some links appear only after those actions, so the crawl doesn't reach them. When only the seed URL gets indexed, AWS's troubleshooting table names these hidden links as the likely cause.
- Bot protection. The crawler identifies itself with its own user agent, so sites that block automated traffic can block it.
- Deleted pages. AWS's docs say the customer-managed crawler may not detect deleted pages, so old content can stay indexed. The managed crawler syncs deletions.
For public pages that allow crawling, a Web Scraping API can handle fetching. For a few sections of your own docs or help center, the crawler is the simplest of the Bedrock knowledge base data sources.
Two ways to feed external web data into a Bedrock knowledge base
Collect the pages outside Bedrock, then sync them from Amazon S3 or push them directly into a custom data source. We ran both on a managed knowledge base with the same 190 pages from the Kubernetes docs.
For a pipeline that runs when a crawl finishes, the custom data source works best. Use S3 when you also want a copy of every page in your bucket. A web scraping API handles the collection step, fetching each URL with proxies and JavaScript rendering handled for you, and returns Markdown that S3 or a custom data source accepts.
Aspect
Amazon S3 data source
Custom data source (direct ingestion)
Ingestion method
Files in a bucket, read by a sync job
Pushed through IngestKnowledgeBaseDocuments
Sync job needed
Yes
No
Knowledge base types
Managed and customer-managed
Managed and customer-managed
Deleting pages
Delete the file, then sync
DeleteKnowledgeBaseDocuments
Copy of the data outside Bedrock
Yes, in your bucket
No, unless you store one yourself
Best for
Scheduled bulk refreshes, audit trail
Frequent small updates triggered by a finished crawl
Sync pages from Amazon S3
Each crawled page becomes one Markdown file in a bucket that the knowledge base uses as an S3 data source. Markdown keeps headings and code blocks without HTML markup, which is why Markdown works well for LLM input. A sync job (StartIngestionJob, or Sync in the console) indexes new and changed files.
Each page file can have a metadata file with the same name plus .metadata.json. That file holds the Bedrock knowledge base metadata, such as the source URL, site, and change time. Retrieval can filter on these fields.
Put the page URL in the metadata file. Retrieve returns it with each chunk, so your app can cite the web page instead of the S3 object.
Direct ingestion also works on an S3 data source. If you use it there, AWS says to write the same files to S3, or the next sync overwrites the pushed documents. AWS also asks you not to run sync jobs and direct ingestion at the same time.
Push pages to a custom data source
A custom data source accepts documents through the IngestKnowledgeBaseDocuments API and indexes them without a sync job. DeleteKnowledgeBaseDocuments removes them. If you use the page URL as the document ID, Retrieve returns that URL as the source.
Send up to 10 documents per call on a managed knowledge base. In testing, larger batches failed with "exceeds the maximum allowed (10)", although the direct-ingestion docs page listed 25.
Crawling external sites into Bedrock with Decodo's Web Data API
Decodo's Web Data API collects a whole site section in one /crawl job and returns each page as Markdown, a format both data sources accept.
You scope a crawl job with select_paths, exclude_paths, max_depth, and sitemap, and set its page count with limit. With JS_retry, pages that need JavaScript get a second request with JavaScript rendering. The geo and locale options set the country and language of each request, for sites that change content by region. The /crawl parameters page lists every option and its current range.

A callback_url receives a webhook when the crawl finishes, so the pipeline processes the pages immediately.
Decodo runs the crawl itself, with proxies and JavaScript rendering, so your AWS account only processes the finished pages. Each job can target a different site, and one knowledge base holds them all, with one function copy per site. The code below converts each site's robots.txt rules, including those for AI search and assistant crawlers, into exclude_paths for the crawl. You decide which sites and paths to crawl and confirm you're allowed to.
The full code at the end runs this pipeline:
- A /map call lists the site's URLs, so you can pick the paths to crawl (see choosing RAG data sources).
- A /crawl job runs with a callback_url set to an AWS Lambda function URL.
- The webhook triggers the function, which strips the site template (repeated menus), hashes each page, and pushes changed pages to the knowledge base.
- The knowledge base indexes the pages, and a Retrieve call returns chunks with each page's source URL.
Testing retrieval quality on the Kubernetes docs
The pipeline ran on the Kubernetes concepts docs (CC BY 4.0). The site also has 24 /_print views, which repeat a whole section on one page, and the code excludes them by default.
The table shows how 3 versions of the pages ranked for the same 15 questions, top 5 results each. Only the 3rd includes the print views:
Pages ingested
Text stored
Correct page ranked first (top-1)
Correct page in top 5
Results that were print views
190 pages, Markdown as crawled
20.5 MB (99.3 MB as HTML)
15 of 15
15 of 15
0 of 75
190 pages, site template removed, as the pipeline stores them
2.6 MB
15 of 15
15 of 15
0 of 75
190 pages, site template removed, plus the site's 24 print views
8.2 MB
9 of 15
15 of 15
47 of 75
In each of the 6 misses, a print view that repeats the correct page ranked first. So exclude URL patterns of duplicate pages in the crawl. With Decodo's Markdown as crawled, every correct page already ranked first. Strip the site template anyway, since it cuts stored text by 87% and prevents site-wide menu changes from counting as page edits.
Keep an evaluation set of questions with known correct pages, and rerun it after each crawl change.
When the built-in crawler is enough
For docs you're allowed to crawl, the built-in Web Crawler is the main alternative. The test ran both from the same seed URL in one managed knowledge base, with the same 15 questions:
Data source, seed URL /docs/concepts/
Correct page ranked first (top-1)
Correct page in top 5
Print views or translated copies in results
Documents indexed
Built-in Web Crawler, API defaults with no syncScope
10 of 15
14 of 15
22 of 75
3,505
Built-in Web Crawler, PATH_SPECIFIC, maxLinksPerUrl 1000, URLs ending in /_print excluded
15 of 15
15 of 15
0 of 75
187
Decodo pipeline from this guide, custom data source
15 of 15
15 of 15
0 of 75
190
AWS's docs name the seed URL's host and path (PATH_SPECIFIC) as the default sync scope. With no syncScope in the API request, though, the crawler also indexed other docs sections and translated copies. Pages from other sections ranked first for 3 questions, and translated copies for 2. With syncScope set explicitly, the crawl stayed inside /docs/concepts/. The console preselects PATH_SPECIFIC, but in API calls always set crawlConfiguration.syncScope to PATH_SPECIFIC.
Each Kubernetes docs page links to over 900 URLs, mostly menu links, so raise maxLinksPerUrl on sites like this. At the default of 100, the scoped crawl indexed 105 pages, and 9 of 15 correct pages ranked first. For your own docs, the tuned built-in crawler matched the pipeline's results on these questions, so use it there.
For external sites, Decodo charges per crawled page, and you pay only for successful requests. The /crawl endpoint page lists current rates, the success rate, and the free plan's monthly allowance. That plan includes /map, /crawl, and /search, enough to run the walkthrough below.
Crawl sites into Bedrock
One /crawl job returns every page as Markdown, ready for S3 or a custom data source, with proxies and JavaScript rendering handled for you.
Keeping a web-fed Bedrock knowledge base fresh
Re-crawl on a schedule that matches how often the source changes, and re-ingest only the pages that changed. With the full code, Amazon EventBridge Scheduler starts each crawl. Use these 3 practices for keeping RAG data fresh in production:
- Hash every page. Store a content hash per URL and skip unchanged pages. Re-crawling the 190 concepts pages 14 hours later flagged 5 as changed. The site's Git history showed the same 5 pages edited in that time. An immediate repeat crawl flagged none.
- Delete explicitly. When a URL no longer appears in the crawl, delete the S3 file or call DeleteKnowledgeBaseDocuments. The code deletes only after a complete crawl, and never when a run would remove over 15% of pages.
- Filter by change date. Store the time a crawl last found new content on each page as a number, and filter with greaterThan when a question needs recent content. To filter by site or section, store each as its own attribute, like the code's site. Managed knowledge bases rejected startsWith filters in testing.
Filling knowledge base gaps from user questions
In a separate test, 10 questions needed pages from the Kubernetes tasks section, which the concepts crawl didn't cover. For those 10, Retrieve still ranked a wrong concepts page first.
A score cutoff alone doesn't show which questions lack a page, because the scores overlapped. Wrong first results scored 0.564 to 0.865, and once the missing pages were added, correct ones scored 0.665 to 0.967.
Run the fill command on questions that users of your app mark as unanswered. It sends the question to Decodo's /search endpoint and uses site: to limit results to the hosts in ALLOWED_SITES. If the first result from those hosts isn't indexed yet, a 1-page /crawl job fetches it, and the pipeline adds it to the knowledge base. As with any web search, check questions for customer or internal data before you run fill.
After fill, the correct page ranked first for 7 of the 10 gap questions. In 2 of the others, fill added the correct page, but a concepts page still ranked above it. In the 3rd, search returned a kubectl reference page instead. Each fill call costs one search request and one crawled page, so crawl the sections users ask about most, and use fill for the rest.
Full crawls don't update or delete pages that fill adds. They're listed in state/gaps.json. To keep them current, move the scheduled crawl to a parent path that covers their sections.
Full code and setup
The pipeline is one Python file. It runs locally for the map, crawl, ask, fill, and process commands. As an AWS Lambda function, it handles the schedule, the webhook, and fill requests from your app. It needs Python 3.13 or later, boto3, and AWS credentials. The commands were tested with AWS CLI 2.36.29, and older versions may lack the managed knowledge base options. A single Decodo Web Data API key, exported as DECODO_API_KEY, covers the /map, /crawl, and /search calls. After you sign up for Decodo, find the key on the Web Data API page of the dashboard. If your AWS credentials come from aws login, boto3 also needs botocore[crt], so install it in the same environment.

Save this policy as lambda-policy.json. The setup script inserts your account and knowledge base IDs and attaches the policy to the Lambda role:
Save the pipeline as web_to_bedrock.py, which maps the site, starts the crawl, receives the webhook, cleans and hashes pages, and loads the knowledge base:
Save this script as setup.sh. It creates the 2 IAM roles, the managed knowledge base, the custom data source, the S3 bucket, and the Lambda function with its URL:
The script builds a managed knowledge base, which is the type the code was tested on. Its knowledge base role has no policy attached. AWS's docs say managed embedding and reranking need no model access, and a custom data source ran without one in testing. For the S3 data source below, you add bucket read access. The script uses us-east-1. To use another AWS Region, change REGION insetup.sh and us-east-1 in lambda-policy.json, and check that managed knowledge bases are available there.
Run these commands in order from the folder with the 3 files, starting with a Python virtual environment. env.sh doesn't store DECODO_API_KEY, so export it again in each new shell. Before the first ask, rerun the list-knowledge-base-documents command until it prints about 190. The crawl and the indexing take a few minutes together:
For the concepts crawl, with print views excluded, the function printed this summary in the Lambda logs:
A URL that returns 404 on the site itself is skipped. Your counts can differ from these by 1 or 2 pages as the docs change. After the pages were indexed, the ask command printed 3 chunks from the right page, and the first one was:
For the drain question, the first ask returned an unrelated concepts page on dynamic resource allocation (dra). Then fill added the task page, and a second ask ranked that page first:
To use an S3 data source instead, first give the knowledge base role read access to the bucket. Bedrock checks that access when it creates the data source. Then create it with pages/ as its inclusion prefix:
Set DS_ID to the ID it prints and ROUTE to s3 in your shell, in env.sh, and in the function's environment variables. In the console, edit just those 2 variables. With the CLI, update-function-configuration replaces all of them, so pass the full set. If you ran the custom data source first, delete state/manifest.json (the stored page hashes) and state/gaps.json (the fill pages) from the bucket. The next crawl and fill then write every page to S3. Delete the custom data source too, or each page appears twice. The same code then writes each page with its .metadata.json file and starts a sync job.
To search only recent content, add a maximum age in days at the end of the ask command, for example 7. The ask command then filters on the changed_at attribute that the pipeline stores. Right after the first crawl, every page counts as new.
Running the pipeline in production
To re-crawl regularly, create a schedule in Amazon EventBridge Scheduler with the function as its target. The console can create the role that lets the schedule invoke the function. Set this JSON as its input:
How you set up schedules depends on what you crawl:
- One section. Give the function 1 schedule, with the same crawl URL every time. The code removes pages that a crawl no longer finds, so a 2nd section on the same function would remove the first section's pages.
- Several sections of one site. Move the crawl URL up to a parent path that covers them all. The earlier pages then stay in the crawl.
- Several sites in one knowledge base. Give each site its own copy of the function and bucket, with the same KB_ID. With the custom data source, the copies can share 1 DS_ID. With S3, give each bucket its own S3 data source. Each copy deletes only the pages in its own state/manifest.json. For each new copy, rename the function, its role, and the bucket in setup.sh and lambda-policy.json. Remove the commands that create the knowledge base, its role, and the data source, and use the existing IDs.
- News sections. Articles leave the listing pages but stay online, so a crawl stops finding them. Set DELETE_MISSING to false in that function's environment variables, and the code keeps them.
Space out each schedule so one run finishes before the next starts.
These runs show how much time and memory the function needs, with its memory set to 1,024 MB:
Run
Pages crawled
Duration
Max memory used
Scheduled re-crawl of the concepts section, 5 pages changed
191
25 seconds
149 MB
Whole /docs section, all pages new, custom data source
1,113
191 seconds
434 MB
Whole /docs section, all pages new, S3 data source
1,113
156 seconds
439 MB
With an S3 data source, the sync job added 5 to 6 minutes for a site this size. With the custom data source, each ingestion request stays under AWS's 6 MB limit, even when 10 long pages share a batch.
The code's CRAWL_LIMIT setting limits each crawl to 2K pages. At the rates in the table, that keeps a run within Lambda's 15-minute limit and the function's memory. For a bigger site, raise CRAWL_LIMIT and run the crawl command without a function URL. Run it in an AWS Fargate task or a CI job that has the function's environment variables and IAM policy. The command then waits for the crawl to finish and processes the pages itself. Use this setup instead of the scheduled function, not alongside it, because both write the same manifest, state/manifest.json.
Running without a function URL also works when your organization blocks public function URLs, because nothing has to reach your account from outside. Before you run setup.sh, remove the 3 commands that set up the function URL, and FUNCTION_URL=$URL from its last line. Then attach the policy in lambda-policy.json to the role that your Fargate task or CI job uses, with your account and knowledge base IDs inserted.
To run fill from your app, invoke the function with a fill key that holds the question, and add ALLOWED_SITES to its environment variables. This works with or without a function URL.
To reprocess a finished crawl, for example, after you fix a function error, run python web_to_bedrock.py process with its crawl ID. The crawl command and the function's log both print that ID.
Security notes
- Function URL. The function URL takes calls without AWS authentication, so Decodo's webhook can reach it. Decodo sends WEBHOOK_SECRET back with each webhook as the passthrough value, and the function rejects any call without it. To avoid a public URL, run without one, as described above.
- Permissions. lambda-policy.json allows the Bedrock actions on 1 knowledge base and the S3 actions on 1 bucket.
- Secrets. setup.sh stores DECODO_API_KEY and WEBHOOK_SECRET as environment variables. Before production, move them to AWS Secrets Manager, which security reviews commonly require. Then read them with GetSecretValue instead of os.environ, and allow secretsmanager:GetSecretValue on those secrets in the function's policy.
- Untrusted pages. Text hidden in a page or a user review can try to instruct the model that reads it, which is called indirect prompt injection. If an agent uses this knowledge base, give it only the tools it needs for the task, and require approval before it sends messages or changes data.
- Questions sent to search. fill sends each question to Decodo's /search endpoint, limited to the hosts in ALLOWED_SITES. Without ALLOWED_SITES, it stops before it searches.
Safeguards built into the pipeline
- robots.txt. The crawl request excludes the paths that a site's robots.txt disallows under the crawl path. That covers rules for all crawlers and for the AI search and assistant crawlers in ROBOTS_AGENTS, and fill skips them too. A knowledge base does a similar job, so the code follows their rules. In a November 2026 check, news sites such as nytimes.com blocked all 6 named AI crawlers site-wide while allowing other crawlers. The code also checks the start URL against robots.txt before it starts a crawl. It reads only Disallow lines, so it can be stricter than a site requires, and it stops if a site refuses the robots.txt request. A site can also refuse requests from your network, and the ALERT line then shows the status code. Add new AI search and assistant crawler names to ROBOTS_AGENTS as they publish them.
- Contact address. Some sites, such as sec.gov, refuse requests without a contact address, so set CONTACT_EMAIL in your shell and in the function's environment variables.
- Crawl limit. The code deletes pages only after a crawl that finished below CRAWL_LIMIT, so a crawl that reaches the limit never removes pages it didn't reach.
- Navigation-only changes. The hash covers each page from its first top-level heading down. So a site-wide menu change, such as a new language in the language menu, doesn't count as an edit.
The function prints a line that starts with ALERT in these cases:
- robots.txt disallows the start URL, or the site refuses the robots.txt request
- the crawl path has too many robots.txt rules and needs narrowing
- a crawl ends with a status other than done, or reaches CRAWL_LIMIT
- the code skips deletes
- more than 10% of pages failed, were too short, or matched an error or bot-check phrase in BLOCK_PAGE
- a page is too large for 1 ingestion request, with the custom data source
- Bedrock failed to index pages in the previous run
The next run sends the pages in the last 2 cases again. If the size alert repeats for the same page, add its path to EXCLUDE or use the S3 data source, which takes larger files. Sites change error and bot-check wording, so add new phrases to BLOCK_PAGE when an alert shows one. Create an Amazon CloudWatch metric filter on ALERT with an alarm that notifies a named owner. Add a 2nd alarm for when no summary line arrives within your schedule's interval, which catches a run that didn't finish.
Cleaning up resources
To remove everything that setup.sh created, run these commands from the same folder. Delete any EventBridge schedule you added as well. After that, you can run setup.sh again:
Bottom line
Since September 30, 2026, you can add a new website to a Bedrock knowledge base through the managed Web Crawler or by crawling it yourself. Use the managed crawler for sites you own or are authorized to crawl, with its scope and link limit tuned. For public sites that allow crawling, run the full code above. It loads web data for RAG pipelines from a Decodo crawl through S3 or a custom data source. Either way, remove duplicate pages before they go into Bedrock knowledge bases.
Fresh web data, fewer gaps
Use /map, /crawl, and /search to find missing pages and keep your knowledge base current.
About the author

Justinas Tamasevicius
Director of Engineering
Justinas Tamaševičius is Director of Engineering with over two decades of expertise in software development. What started as a self-taught passion during his school years has evolved into a distinguished career spanning backend engineering, system architecture, and infrastructure development.
Connect with Justinas via LinkedIn.
All information on Decodo Blog is provided on an as is basis and for informational purposes only. We make no representation and disclaim all liability with respect to your use of any information contained on Decodo Blog or any third-party websites that may belinked therein.


