# Submission Methods

## Types of Submissions

[Bundles](../bundles-backruns) can be submitted to the[ FastLane Relay RPC endpoint](../addresses-and-endpoints) 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`](../../../reference/relay-json-rpc-api#pfl_addsearcherbundle) for Bundles



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

{% content-ref url="../../reference/relay-json-rpc-api" %}
[relay-json-rpc-api](../../reference/relay-json-rpc-api)
{% 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 %}

