# Submission Methods

## Types of Submissions

[Bundles](/polygon-fastlane/searcher-guides/bundles-backruns.md) can be submitted to the[ FastLane Relay RPC endpoint](/polygon-fastlane/searcher-guides/addresses-and-endpoints.md) using JSON-RPC 2.0 over HTTPS and Websocket.

Searchers can send their submissions to the FastLane Relay API by calling the methods [`pfl_addSearcherBundle`](/polygon-fastlane/reference/relay-json-rpc-api.md#pfl_addsearcherbundle) for Bundles

More information on the FastLane Relay API and API calls can be found here:

{% content-ref url="/pages/3ARcKJILCx5XNjHmFJ6r" %}
[Relay JSON-RPC API](/polygon-fastlane/reference/relay-json-rpc-api.md)
{% endcontent-ref %}

{% hint style="info" %}
Rate limits (for both HTTP and websocket):

Request/message limit per second: 2

Burst limit: 32

Repeatedly going over the rate limit will result in temporary IP ban, ranging from 3 minutes to 24 hours.
{% endhint %}

## HTTPS

{% hint style="info" %}
It's recommended that you use HTTP Keep-Alive connections to connect to the RPC endpoint to minimize TLS connection establishment times as it significantly reduces request latency when sending a bundle if the TLS handshake is already complete.

The `/ping` endpoint is available for establishing a session, you can have a maximum of 2 sessions per IP address, and sessions will persist for a maximum of 15 minutes.
{% endhint %}

**Example:**

{% tabs %}
{% tab title="Javascript" %}

```typescript
import axios from 'axios';

// Abstracted function to create the PFL bundle
function createPflBundle(bundleId, opportunityRawTx, solverOpStruct) {
  return {
    id: bundleId,
    jsonrpc: "2.0",
    method: "pfl_addSearcherBundle",
    params: [opportunityRawTx, solverOpStruct]
  };
}

// Helper function to submit the bundle to the Fastlane endpoint
async function submitBundle(bundle) {
  const httpFastlaneEndpoint = "https://polygon-rpc.fastlane.xyz";

  try {
    const response = await axios.post(httpFastlaneEndpoint, bundle, {
      timeout: 1000
    });

    if (response.data.error) {
      console.error(`Error submitting bundle ${bundle.id}`, response.data);
    } else {
      console.log(`Response received for bundle ${bundle.id}`, response.data);
    }
  } catch (error) {
    console.error(`Error submitting bundle ${bundle.id}`, error);
  }
}

// Main function to submit the bundle
async function main() {
  // Define your variables
  const bundleId = 1;
  const opportunityRawTx = "0x..."; // Replace with your actual opportunity raw transaction
  const solverOpStruct = "0x...";   // Replace with your actual solver operation structure

  // Create the PFL bundle
  const pflBundle = createPflBundle(bundleId, opportunityRawTx, solverOpStruct);

  // Submit the bundle to the Fastlane endpoint
  await submitBundle(pflBundle);
}

main().catch(console.error);
```

{% endtab %}

{% tab title="Python" %}

```python
import asyncio
import aiohttp
import json

# Asynchronous function to test the connection to the Fastlane endpoint
async def ping_fastlane():
    http_fastlane_ping_endpoint = "https://polygon-rpc.fastlane.xyz/ping"
    try:
        async with aiohttp.ClientSession() as session:
            async with session.get(http_fastlane_ping_endpoint, timeout=10) as response:
                if response.status == 200:
                    text = await response.text()
                    print("Ping successful:", text)
                else:
                    print("Ping failed with status code:", response.status)
    except aiohttp.ClientError as e:
        print("Ping failed:", e)
    except asyncio.TimeoutError:
        print("Ping failed: Request timed out")

# Function to create the PFL bundle with specified arguments
def create_pfl_bundle(bundle_id, opportunity_raw_tx, solver_op_struct):
    return {
        "id": bundle_id,
        "jsonrpc": "2.0",
        "method": "submitBundle",
        "params": [opportunity_raw_tx, solver_op_struct]
    }

# Asynchronous function to submit the bundle to the Fastlane endpoint
async def submit_bundle(bundle):
    http_fastlane_endpoint = "https://polygon-rpc.fastlane.xyz"
    try:
        async with aiohttp.ClientSession() as session:
            async with session.post(http_fastlane_endpoint, json=bundle, timeout=10) as response:
                response.raise_for_status()  # Raise an error for bad HTTP status codes
                response_data = await response.json()
                if 'error' in response_data:
                    print(f"Error submitting bundle {bundle['id']}: {response_data['error']}")
                else:
                    print(f"Response received for bundle {bundle['id']}: {response_data['result']}")
    except aiohttp.ClientResponseError as http_err:
        print(f"HTTP error occurred while submitting bundle {bundle['id']}: {http_err}")
    except aiohttp.ClientError as client_err:
        print(f"Client error occurred while submitting bundle {bundle['id']}: {client_err}")
    except asyncio.TimeoutError:
        print(f"Error submitting bundle {bundle['id']}: Request timed out")
    except json.JSONDecodeError as json_err:
        print(f"JSON decode error for bundle {bundle['id']}: {json_err}")

# Asynchronous main function to test connection and submit the bundle
async def main():
    # Test the connection to the Fastlane endpoint
    await ping_fastlane()
    
    # Wait until you're ready to send a bundle, refreshing session as necessary
    # You might include a delay or a condition here if needed
    
    # Define your variables
    bundle_id = 1
    opportunity_raw_tx = "0x..."  # Replace with your actual opportunity raw transaction
    solver_op_struct = "0x..."    # Replace with your actual solver operation structure
    
    # Create the PFL bundle with the specified arguments
    pfl_bundle = create_pfl_bundle(bundle_id, opportunity_raw_tx, solver_op_struct)
    
    # Submit the bundle to the Fastlane endpoint
    await submit_bundle(pfl_bundle)

if __name__ == "__main__":
    asyncio.run(main())
```

{% endtab %}
{% endtabs %}

## Websocket

{% hint style="info" %}
**`Ping`** messages will be sent to your connection every 60 seconds. A **`pong`** response must be sent to avoid disconnection. Alternatively, you can send **`ping`** messages yourself to the server every minutes and get **`pong`** responses.

To keep a websocket connection open and stable on a long term, we highly recommend to send **`ping`** messages to the server every minutes, and to answer **`pong`** every time the server sends a **`ping`** message.
{% endhint %}

{% hint style="info" %}
To better track responses (e.g. when sending multiple concurrent requests), it is highly advised to specify a unique `id` for each new requests, see example below.
{% endhint %}

**Example:**

{% tabs %}
{% tab title="Javascript" %}

```javascript
const WebSocket = require('ws');

// Abstracted function to create the PFL bundle with arguments
function createPflBundle(bundleId, opportunityRawTx, solverOpStruct) {
  return {
    id: bundleId,
    jsonrpc: "2.0",
    method: "pfl_addSearcherBundle",
    params: [`${opportunityRawTx}`, `${JSON.stringify(solverOpStruct)}`]
  };
}

// Helper function to submit the bundle to the Fastlane endpoint over WebSocket
async function submitBundle(bundle) {
  const wsFastlaneEndpoint = "wss://beta-rpc.fastlane-labs.xyz/ws";

  return new Promise((resolve, reject) => {
    const ws = new WebSocket(wsFastlaneEndpoint);

    ws.on('open', function open() {
      // Send the bundle as a JSON-RPC request
      ws.send(JSON.stringify(bundle));
    });

    ws.on('message', function incoming(data) {
      const response = JSON.parse(data);
      if (response.error) {
        console.error(`Error submitting bundle ${bundle.id}`, response);
        ws.close();
        reject(response.error);
      } else {
        console.log(`Response received for bundle ${bundle.id}`, response);
        ws.close();
        resolve(response.result);
      }
    });

    ws.on('error', function error(err) {
      console.error(`WebSocket error for bundle ${bundle.id}`, err);
      ws.close();
      reject(err);
    });

    ws.on('close', function close(code, reason) {
      console.log(`WebSocket connection closed: ${code} ${reason}`);
    });
  });
}

// Main function to submit the bundle
async function main() {
  // Define your variables
  const bundleId = 1;
  const opportunityRawTx = "0x..."; // Replace with your actual opportunity raw transaction
  const solverOpStruct = "0x...";   // Replace with your actual solver operation structure

  // Create the PFL bundle
  const pflBundle = createPflBundle(bundleId, opportunityRawTx, solverOpStruct);

  // Submit the bundle to the Fastlane endpoint
  await submitBundle(pflBundle);
}

main().catch(console.error);
```

{% endtab %}

{% tab title="Python" %}
{% code fullWidth="false" %}

```python
import asyncio
import websockets
import json

# Function to create the PFL bundle with arguments
def create_pfl_bundle(bundleId, opportunityRawTx, solverOpStruct):
    return {
        "id": bundleId,
        "jsonrpc": "2.0",
        "method": "pfl_addSearcherBundle",  # Ensure the method name matches the JS version
        "params": [
            f"{opportunityRawTx}",                   # Convert opportunityRawTx to string
            f"{json.dumps(solverOpStruct)}"          # Serialize solverOpStruct to JSON string
        ]
    }

# Helper function to submit the bundle via WebSocket
async def submit_bundle(bundle):
    websocket_endpoint = "wss://polygon-rpc.fastlane.xyz"
    try:
        async with websockets.connect(websocket_endpoint) as websocket:
            await websocket.send(json.dumps(bundle))
            response = await websocket.recv()
            response_data = json.loads(response)
            if 'error' in response_data:
                print(f"Error submitting bundle {bundle['id']}:", response_data['error'])
            else:
                print(f"Response received for bundle {bundle['id']}:", response_data['result'])
    except Exception as e:
        print(f"Error submitting bundle {bundle['id']}:", e)

# Main function to submit the bundle
async def main():
    # Wait until you're ready to send a bundle, refreshing session as necessary
    # You might want to include some delay or conditions here
    
    # Define your variables
    bundleId = 1
    opportunityRawTx = "0x..."  # Replace with your actual opportunity raw transaction
    solverOpStruct = "0x..."    # Replace with your actual solver operation structure
    
    # Create the PFL bundle with the specified arguments
    pfl_bundle = create_pfl_bundle(bundleId, opportunityRawTx, solverOpStruct)
    
    # Submit the bundle to the Fastlane endpoint
    await submit_bundle(pfl_bundle)

if __name__ == "__main__":
    asyncio.run(main())
```

{% endcode %}
{% endtab %}
{% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://fastlane-labs.gitbook.io/polygon-fastlane/searcher-guides/getting-started-as-a-searcher/submission-methods.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
