# How to Make HTTP GET Requests from a GenLayer Contract: Complete Implementation Guide

> Learn how to make HTTP GET requests from GenLayer contracts using the gl.nondet.web.get API. Our guide explains the leader-validator consensus pattern for secure external data fetching and verification.

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

---

**GenLayer contracts execute HTTP GET requests using the `gl.nondet.web.get` API within a leader-validator consensus pattern, where the leader fetches external data and validators independently verify the response to ensure deterministic consensus.**

The genlayer-project-boilerplate repository provides the foundational patterns for integrating external HTTP APIs into intelligent contracts. Making HTTP GET requests from a GenLayer contract requires understanding non-deterministic execution contexts, where network responses must be validated across the validator network to maintain blockchain integrity.

## Understanding Non-Deterministic Web Calls

External HTTP requests are inherently non-deterministic because different nodes may receive varying responses, timestamps, or server errors. The GenLayer SDK wraps these calls in a consensus mechanism that requires explicit validation logic. According to the source code in [`CLAUDE.md`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/CLAUDE.md), the `gl.nondet.web.get` function returns a `Response` object containing `status`, `headers`, and `body` bytes, but this data cannot be stored directly without validator agreement.

### The Leader-Validator Architecture

Every HTTP GET operation requires defining two distinct functions:

- **Leader function**: Executes the initial `gl.nondet.web.get` call, processes the response (typically decoding bytes to strings), and proposes the result to the network.
- **Validator function**: Re-issues the identical HTTP request and confirms the response matches the leader's proposed result, returning `True` only if the data matches exactly.

This pattern ensures that only consensus-validated external data enters the contract state, preventing malicious or corrupted API responses from affecting the network.

## Implementing HTTP GET Requests with gl.nondet.web.get

The primary API signature for fetching data is defined in [`CLAUDE.md`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/CLAUDE.md) as:

```python
gl.nondet.web.get(url: str, *, headers: dict = {}) -> Response

```

The `Response` object contains `status` (int), `headers` (dict), and `body` (bytes) attributes. When implementing contracts that use this API, you must decode the body bytes before storage or comparison.

### Basic Fetch-and-Store Contract

The following implementation from [`tests/integration/test_new_features.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/integration/test_new_features.py) demonstrates the complete pattern for making HTTP GET requests in a production contract:

```python
from genlayer import *
import genlayer.gl.vm as glvm

class FetchContract(gl.Contract):
    last_result: str

    def __init__(self):
        self.last_result = ""

    @gl.public.write
    def fetch_and_store(self, url: str) -> str:
        # ---------- Leader ----------

        def leader() -> str:
            resp = gl.nondet.web.get(url)
            return resp.body.decode("utf-8", errors="replace")

        # ---------- Validator ----------

        def validator(result: glvm.Result) -> bool:
            if not isinstance(result, glvm.Return):
                return False
            resp = gl.nondet.web.get(url)
            return resp.body.decode("utf-8", errors="replace") == result.calldata

        # Execute consensus

        value = glvm.run_nondet_unsafe.lazy(leader, validator).get()
        self.last_result = value
        return value

```

**Key implementation details:**

- **Use `run_nondet_unsafe`** when the leader function does not call `spawn_sandbox`; otherwise use `run_nondet`.
- **Always decode bytes** using `resp.body.decode()` with error handling to convert HTTP payloads to contract-compatible strings.
- **Store state** only after the validator consensus completes successfully via the `.get()` call.

## Testing HTTP GET Requests in Local Development

The repository includes direct-mode testing utilities that mock HTTP responses, eliminating external network dependencies during development. The test file [`tests/integration/test_new_features.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/integration/test_new_features.py) demonstrates this workflow:

```python
def test_fetch_and_store(direct_vm, direct_deploy, nondet_contract_path):
    # Configure the mock response

    direct_vm.mock_web(r".*api.example.com.*", {
        "method": "GET",
        "status": 200,
        "body": b"hello world",
    })

    # Deploy and execute

    contract = direct_deploy(nondet_contract_path)
    result = contract.fetch_and_store("https://api.example.com/data")

    assert result == "hello world"
    assert contract.last_result == "hello world"
    
    # Verify validator consensus

    assert direct_vm.run_validator() is True

```

**Testing workflow:**

1. **Mock the endpoint** using `direct_vm.mock_web` with regex patterns to match URLs.
2. **Define deterministic responses** including status codes and byte payloads.
3. **Validate consensus** by calling `direct_vm.run_validator()` to ensure the validator function correctly approves the leader's result.

## Real-World Integration Patterns

While [`contracts/football_bets.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/football_bets.py) primarily demonstrates `gl.nondet.web.render` for HTML retrieval, the pattern for raw GET requests follows identical structural logic. When you need JSON or binary data instead of rendered HTML, substitute `get` for `render`:

```python
def fetch_api_data(self, api_endpoint: str) -> dict:
    # Fetch raw JSON instead of rendered HTML

    resp = gl.nondet.web.get(api_endpoint)
    raw_data = resp.body.decode("utf-8", errors="replace")
    # Process data (e.g., parse JSON or pass to LLM)

    return processed_result

```

This approach maintains the same leader-validator requirements while enabling integration with REST APIs, price oracles, or any external HTTP service.

## Summary

- **Use `gl.nondet.web.get`** to perform HTTP GET requests from within GenLayer contracts, importing from the core SDK as shown in [`CLAUDE.md`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/CLAUDE.md).
- **Implement leader-validator pairs** to handle non-deterministic external calls, ensuring validators re-issue identical requests and compare responses byte-for-byte.
- **Execute consensus** using `glvm.run_nondet_unsafe` (or `glvm.run_nondet` when using sandboxes) to validate results before storage.
- **Test locally** with `direct_vm.mock_web` to simulate API responses and verify consensus logic without network latency.
- **Reference [`contracts/football_bets.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/football_bets.py)** for complex contract patterns, substituting `get` for `render` when raw HTTP responses are required.

## Frequently Asked Questions

### What is the difference between gl.nondet.web.get and gl.nondet.web.render?

**`gl.nondet.web.get` returns raw HTTP response bytes**, allowing contracts to process JSON, XML, or binary data, while `gl.nondet.web.render` (as used in [`contracts/football_bets.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/football_bets.py)) executes JavaScript rendering on the target page and returns the final HTML. Use `get` for API endpoints and `render` for single-page applications requiring browser execution.

### Why must validators re-issue the same HTTP request?

Validators independently fetch the URL to **verify the leader's honesty** and ensure network consensus. Because external APIs can change between calls or return different data to different nodes, the validator's comparison function confirms that the leader's proposed result matches a fresh, independently obtained response. This prevents compromised leaders from injecting false data into the blockchain state.

### When should I use run_nondet_unsafe versus run_nondet?

**Use `run_nondet_unsafe`** when the leader function does not call `spawn_sandbox`, which is the typical pattern for simple HTTP GET requests. According to the source implementation, `run_nondet` is required only when the leader creates isolated execution environments; otherwise, the unsafe variant provides the necessary consensus validation without sandbox overhead.

### How do I handle HTTP headers in GenLayer GET requests?

Pass headers as a dictionary to the `headers` keyword argument: `gl.nondet.web.get(url, headers={"Authorization": "Bearer token", "Accept": "application/json"})`. The validator function must include identical headers when re-issuing the request to ensure consistent comparison results. Store any required authentication tokens in contract state or pass them as method arguments to both leader and validator functions.