# Accessing a Public API

Data Requests allow you to retrieve information from public API endpoints that do not require sensitive credentials like API keys. For APIs that do require keys, see [accessing-a-private-api](https://docs.seda.xyz/home/for-developers/define-your-data-source/accessing-a-private-api "mention").

In this walkthrough, we'll use the [SEDA Starter Kit](https://github.com/sedaprotocol/seda-starter-kit), which includes all necessary dependencies to build and test an Oracle Program.

## HTTP Fetch: GET

The `httpFetch` method is available only during the **execution** phase.

{% hint style="warning" %}
Attempting to use it in the tally phase will cause an error due to its non-deterministic nature.
{% endhint %}

Example using the `http_fetch` method:

```rust
use seda_sdk_rs::{http_fetch, log, oracle_program, Process};

#[oracle_program]
impl MyDataRequest {
    fn execute() {
        let response = http_fetch("https://pokeapi.co/api/v2/pokemon-species/mewtwo", None);

        if !response.is_ok() {
            Process::error("Could not fetch API endpoint".as_bytes());
            return;
        }
        let response_str = String::from_utf8(response.bytes).unwrap();
        log!("Response: {}", response_str);
    }

    fn tally() {
        panic!("Not implemented");
    }
}
```

Running this program with the following test will output the result of the execution, showing the `http_fetch` response as a string. You can place this test in `tests/my-dr.test.ts`:

```typescript
import { describe, it} from "bun:test";
import { file } from "bun";
import { testOracleProgramExecution } from "@seda-protocol/dev-tools"

describe("data request execution", () => {
  it("should run MyDataRequest", async () => {
    const wasmBinary = await file("target/wasm32-wasip1/release-wasm/oracle-program.wasm").arrayBuffer();

    // Locally executes the Data Request
    const vmResult = await testOracleProgramExecution(
      Buffer.from(wasmBinary),
      // This program doesn't have any inputs
      Buffer.from(""),
    );
    
    console.log(vmResult.stdout);
    console.error(vmResult.stderr);
  });
});
```

#### Testing the Data Request

Running the test will compile the binary and execute the test file, showing the output of the API formatted in JSON.

```sh
$ bun run test
```

Outputs:

```bash
bun test v1.2.5 (013fdddc)

tests/index.test.ts:
Response: [...]

✓ data request execution > should run MyDataRequest [1102.00ms]
```

#### Parsing JSON Responses

To make your Oracle Program more useful, you can parse the JSON response and work with its properties. You can use the `serde` crate to deserialize API responses into custom Rust structs.

Let’s modify our example:

```rust
use seda_sdk_rs::{http_fetch, log, oracle_program, Process};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct Color {
    name: String,
}

#[derive(Serialize, Deserialize)]
struct PokemonSpecies {
    id: u32,
    name: String,
    color: Color,
    is_legendary: bool,
}

#[oracle_program]
impl MyDataRequest {
    fn execute() {
        let response = http_fetch("https://pokeapi.co/api/v2/pokemon-species/mewtwo", None);

        if !response.is_ok() {
            Process::error("Could not fetch API endpoint".as_bytes());
            return;
        }
        let species = serde_json::from_slice::<PokemonSpecies>(&response.bytes).unwrap();

        log!(
            "Pokemon: {} (ID: {}), Color: {}, Legendary: {}",
            species.name,
            species.id,
            species.color.name,
            species.is_legendary
        );
    }

    fn tally() {
        panic!("Not implemented");
    }
}
```

Running the previous test again now outputs:

```sh
bun test v1.2.5 (013fdddc)

tests/index.test.ts:
Pokemon: mewtwo (ID: 150), Color: purple, Legendary: true

✓ data request execution > should run MyDataRequest [723.00ms]
```

Now you can call any open API you want and process the data as needed.

## HTTP Fetch: POST

The SEDA SDK supports `POST`, `PATCH`, `PUT`, and other HTTP methods via `HttpFetchOptions`. You can attach headers and a request body — useful for working with GraphQL APIs or file uploads (e.g., IPFS).

Example sending JSON via POST:

```rust
use std::collections::BTreeMap;

use seda_sdk_rs::{
    bytes::{Bytes, ToBytes},
    elog, http_fetch, log, oracle_program, HttpFetchMethod, HttpFetchOptions, Process,
};
use serde_json::json;

#[oracle_program]
impl MyDataRequest {
    fn execute() {
        let query_body = json!({
            "key": "value_test"
        });

        let mut headers: BTreeMap<String, String> = BTreeMap::default();
        headers.insert("content-type".to_string(), "application/json".to_string());

        let body: Bytes = query_body.to_string().to_bytes();
        let response = http_fetch(
            "https://httpbin.org/post",
            Some(HttpFetchOptions {
                method: HttpFetchMethod::Post,
                headers,
                body: Some(body),
            }),
        );

        if !response.is_ok() {
            elog!("Error: {}", String::from_utf8_lossy(&response.bytes));
            Process::error("Could not fetch API endpoint".as_bytes());
            return;
        }

        log!("Success: {}", String::from_utf8(response.bytes).unwrap());
    }

    fn tally() {
        panic!("Not implemented");
    }
}
```

Running the test will give you the response of the POST request:

```sh
bun test v1.1.26 (0a37423b)

tests/index.test.mts:
Success: {
  "args": {}, 
  "data": "{\"key\":\"value_test\"}", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate, br", 
    "Content-Length": "20", 
    "Content-Type": "application/json", 
    "Host": "httpbin.org", 
    "User-Agent": "Bun/1.2.5", 
  }, 
  "json": {
    "key": "value_test"
  }, 
  "origin": "REDACTED", 
  "url": "https://httpbin.org/post"
}
✓ data request execution > should run MyDataRequest [883.96ms]
```
