# How to Implement Web Requests with gl.nondet.web.render in GenLayer Smart Contracts

> Learn to implement web requests in GenLayer smart contracts using gl.nondet.web.render. Fetch external content and process it with LLMs for deterministic consensus. Explore the GenLayer project boilerplate for hands on examples.

- Repository: [GenLayer Labs/genlayer-project-boilerplate](https://github.com/genlayerlabs/genlayer-project-boilerplate)
- Tags: how-to-guide
- Published: 2026-08-20

---

**You can implement web requests in GenLayer smart contracts by calling `gl.nondet.web.render` to fetch external page content, then processing that content through an LLM prompt wrapped in an equivalence-principle verification to maintain deterministic consensus.**

The `genlayer-project-boilerplate` repository demonstrates this exact pattern in production contract code. Learning how to implement web requests with `gl.nondet.web.render` in GenLayer allows your smart contracts to ingest real-world data while preserving the blockchain's deterministic guarantees.

## How to Implement Web Requests with gl.nondet.web.render

GenLayer contracts call external web resources through the non-deterministic API `gl.nondet.web`. The architectural flow follows five concrete steps to keep the contract deterministic while still allowing dynamic web data:

1. **Call `gl.nondet.web.render`** to fetch raw page content.
2. **Compose an LLM prompt** that instructs the model to output only JSON.
3. **Execute the prompt** with `gl.nondet.exec_prompt(..., response_format="json")`.
4. **Validate** the LLM output using an equivalence-principle function such as `gl.eq_principle.strict_eq`.
5. **Parse** the verified JSON and use the data inside the contract logic.

### Fetch Page Content Using gl.nondet.web.render

The `render` method retrieves a page's content in either **text** or **HTML** form. In [`contracts/football_bets.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/football_bets.py), the private `_check_match` helper uses `mode="text"` to strip markup and obtain plain text suitable for LLM consumption.

```python
from genlayer import *

def fetch_match_text(url: str) -> str:
    # Retrieve the page as plain text (no HTML tags)

    return gl.nondet.web.render(url, mode="text")

```

### Compose an LLM Prompt for Structured Extraction

After fetching raw page content, build an LLM prompt that instructs the model to output only JSON. The prompt should embed the web data alongside explicit schema requirements.

```python
def extract_match_info(url: str, team1: str, team2: str) -> dict:
    # 1️⃣ Get raw page content

    page = fetch_match_text(url)

    # 2️⃣ Build an LLM prompt that forces JSON output

    prompt = f"""
    Extract the match result for:
    Team 1: {team1}
    Team 2: {team2}
    
    Web content:
    {page}
    
    Respond in JSON:
    {{
        "score": str,   // e.g. "1:2" or "-" if unresolved
        "winner": int   // 0 for draw, -1 if unresolved
    }}
    It is mandatory that you respond only using the JSON format above,
    nothing else.
    """

    # 3️⃣ Execute the prompt (JSON response format)

    llm_result = gl.nondet.exec_prompt(prompt, response_format="json")

    # 4️⃣ Verify output with strict equivalence principle

    verified = json.loads(gl.eq_principle.strict_eq(lambda: json.dumps(llm_result, sort_keys=True)))

    return verified

```

### Validate Output with the Equivalence Principle

Because web fetches and LLM calls are non-deterministic, GenLayer requires an **equivalence-principle function** to ensure validators agree on the result. The boilerplate uses `gl.eq_principle.strict_eq` to verify that repeated executions produce identical JSON output before the contract accepts the data.

### Parse Verified Data Inside the Contract

Once sanitized, parse the JSON and use the fields directly in your contract logic. The `FootballBets` contract demonstrates this inside its `_check_match` method, located in [`contracts/football_bets.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/football_bets.py) (lines 30-33).

```python
class FootballBets(gl.Contract):
    # ...

    def _check_match(self, resolution_url: str, team1: str, team2: str) -> dict:
        # The core web-render + LLM extraction logic

        def get_match_result() -> str:
            web_data = gl.nondet.web.render(resolution_url, mode="text")
            task = f'''
                Extract the match result for:
                Team 1: {team1}
                Team 2: {team2}
                
                Web content:
                {web_data}
                
                Respond in JSON:
                {{
                    "score": str,
                    "winner": int
                }}
                Only output the JSON.
            '''
            result = gl.nondet.exec_prompt(task, response_format="json")
            return json.dumps(result, sort_keys=True)

        # Verify and parse

        result_json = json.loads(gl.eq_principle.strict_eq(get_match_result))
        return result_json

```

## Key Files in the genlayer-project-boilerplate

The repository contains several files that illustrate non-deterministic web requests and their surrounding infrastructure:

- [`contracts/football_bets.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/football_bets.py) — Shows a real-world use of `gl.nondet.web.render` (lines 30-33) and the surrounding equivalence-principle flow.
- [`contracts/PatternTest.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/PatternTest.py) — Contains additional examples of non-deterministic calls and pattern matching.
- [`config/genlayer_config.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/config/genlayer_config.py) — Loads environment variables required for RPC connectivity (used by the test harness).

## Summary

- **`gl.nondet.web.render`** fetches external page content in `text` or `HTML` mode for use inside smart contracts.
- The **equivalence principle** (`gl.eq_principle.strict_eq`) is mandatory to sanitize non-deterministic web and LLM output so validators can reach consensus.
- The `FootballBets` contract in [`contracts/football_bets.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/football_bets.py) demonstrates the complete fetch-prompt-verify-parse pattern.
- Always force a structured response format such as `response_format="json"` when calling `gl.nondet.exec_prompt` to simplify downstream parsing.

## Frequently Asked Questions

### What does gl.nondet.web.render return?

The method returns raw page content for a given URL in either **text** or **HTML** form based on the `mode` parameter. When `mode="text"` is supplied, HTML tags are stripped so the output can be fed directly into an LLM prompt.

### Why is the equivalence principle required for web requests?

Web pages and LLM responses are non-deterministic and may vary across validators. GenLayer uses equivalence-principle functions such as `gl.eq_principle.strict_eq` to ensure all validators agree on the exact same output before it is recorded on-chain.

### Can gl.nondet.web.render fetch HTML instead of text?

Yes. According to the GenLayer API as implemented in the boilerplate, `gl.nondet.web.render` supports both text and HTML retrieval modes. The `FootballBets` contract uses `mode="text"` because plain text is easier to parse inside LLM prompts.

### Where is the FootballBets contract located?

The complete implementation is located in [`contracts/football_bets.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/football_bets.py) within the `genlayer-project-boilerplate` repository. This file demonstrates the `_check_match` method combining `gl.nondet.web.render`, `gl.nondet.exec_prompt`, and `gl.eq_principle.strict_eq` to resolve match results.