Backend Development 12 min read

Build a Lightweight Python GUI for Testing REST and SOAP APIs (with .exe Packaging)

This tutorial walks you through creating a compact Python Tkinter GUI that can send GET/POST requests, handle JSON or XML payloads, support Bearer tokens, clean illegal URL characters, and be packaged into a standalone Windows executable using PyInstaller.

Test Development Learning Exchange
Test Development Learning Exchange
Test Development Learning Exchange
Build a Lightweight Python GUI for Testing REST and SOAP APIs (with .exe Packaging)

In everyday development we often need to test backend Web APIs, whether RESTful or SOAP. While tools like Postman exist, a lightweight, customizable, and standalone GUI can be more convenient for specific scenarios.

Why build your own tool? It offers a minimal dependency footprint, allows custom features such as token handling and XML payloads, and runs independently of a browser.

Key features include:

Support for GET and POST methods

Customizable request headers

JSON and XML request bodies

Bearer token authentication

Automatic cleaning of illegal URL characters

Modern GUI using ttkthemes

Ability to package as a Windows .exe without requiring a Python runtime

The interface is divided into several sections: a URL input field, method selector (GET/POST), content‑type selector (JSON/XML), optional token input, header editor, body editor, and a response display area showing status code and response content.

Full Python Code (with comments)

<code>import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox
import requests
from ttkthemes import ThemedStyle
import re

def send_request():
    raw_url = entry_url.get()
    method = var_method.get()
    headers_text = entry_headers.get("1.0", tk.END)
    body_text = entry_body.get("1.0", tk.END).strip()
    token = entry_token.get().strip()
    use_token = var_use_token.get()
    content_type = var_content_type.get()  # "JSON" or "XML"
    # Clean URL
    url = raw_url.strip()
    url = ''.join(c for c in url if '\u0020' <= c <= '\uFFFF')
    if not url.startswith(("http://", "https://")):
        messagebox.showerror("Error", "Please enter a valid URL starting with http:// or https://")
        return
    try:
        headers = {}
        for line in headers_text.strip().split('\n'):
            if ':' in line:
                key, value = line.split(':', 1)
                headers[key.strip()] = value.strip()
        # Add token if needed
        if use_token and token:
            headers["Authorization"] = f"Bearer {token}"
        # Set Content-Type
        if content_type == "JSON":
            headers["Content-Type"] = "application/json"
        elif content_type == "XML":
            headers["Content-Type"] = "application/xml"
        # Send request
        if method == "GET":
            response = requests.get(url, headers=headers)
        elif method == "POST":
            if content_type == "JSON":
                import json
                try:
                    json_body = json.loads(body_text)
                    response = requests.post(url, headers=headers, json=json_body)
                except json.JSONDecodeError as e:
                    messagebox.showerror("JSON Error", str(e))
                    return
            elif content_type == "XML":
                response = requests.post(url, headers=headers, data=body_text)
        else:
            messagebox.showerror("Error", "Unsupported method!")
            return
        text_response.delete("1.0", tk.END)
        text_response.insert(tk.END, f"Status Code: {response.status_code}\n\n")
        try:
            text_response.insert(tk.END, response.json())
        except Exception:
            text_response.insert(tk.END, response.text)
    except Exception as e:
        messagebox.showerror("Error", str(e))

# ================= GUI ===================
app = tk.Tk()
app.title("WebService Test Tool - JSON/XML Support")
app.geometry("950x800")
app.resizable(True, True)
style = ThemedStyle(app)
style.set_theme("arc")
bg_color = "#f2f2f2"
fg_color = "#333"
accent_color = "#4a90e2"
style.configure("TButton", padding=6, relief="flat", background=accent_color, foreground=fg_color, font=('Segoe UI', 10))
style.map("TButton", background=[('active', '#357ABD')], foreground=[('pressed', 'white'), ('active', 'white')])
style.configure("TRadiobutton", background=bg_color, font=('Segoe UI', 10))
style.configure("TLabel", background=bg_color, font=('Segoe UI', 10))
style.configure("TCheckbutton", background=bg_color, font=('Segoe UI', 10))
app.configure(bg=bg_color)

# Token area
frame_token = ttk.Frame(app)
frame_token.pack(pady=10, padx=20, fill='x')
var_use_token = tk.BooleanVar()
check_use_token = ttk.Checkbutton(frame_token, text="Use Token", variable=var_use_token)
check_use_token.pack(side='left', padx=10)
ttk.Label(frame_token, text="Token:").pack(side='left')
entry_token = ttk.Entry(frame_token, width=50, show="*", font=('Segoe UI', 10))
entry_token.pack(side='left', padx=5, fill='x', expand=True)

# URL input
frame_url = ttk.Frame(app)
frame_url.pack(pady=10, padx=20, fill='x')
ttk.Label(frame_url, text="URL:").pack(anchor="w")
entry_url = ttk.Entry(frame_url, width=80, font=('Segoe UI', 11))
entry_url.pack(fill='x')

# Method selection
frame_method = ttk.Frame(app)
frame_method.pack(pady=5, padx=20, fill='x')
ttk.Label(frame_method, text="Method:").pack(anchor="w")
var_method = tk.StringVar(value="GET")
ttk.Radiobutton(frame_method, text="GET", variable=var_method, value="GET").pack(side="left", padx=10)
ttk.Radiobutton(frame_method, text="POST", variable=var_method, value="POST").pack(side="left", padx=10)

# Content type selection
frame_content_type = ttk.Frame(app)
frame_content_type.pack(pady=5, padx=20, fill='x')
ttk.Label(frame_content_type, text="Content Type:").pack(anchor="w")
var_content_type = tk.StringVar(value="JSON")
ttk.Radiobutton(frame_content_type, text="JSON", variable=var_content_type, value="JSON").pack(side="left", padx=10)
ttk.Radiobutton(frame_content_type, text="XML", variable=var_content_type, value="XML").pack(side="left", padx=10)

# Headers input
frame_headers = ttk.Frame(app)
frame_headers.pack(pady=10, padx=20, fill='x')
ttk.Label(frame_headers, text="Headers (Key: Value)").pack(anchor="w")
entry_headers = scrolledtext.ScrolledText(frame_headers, height=5, wrap=tk.WORD, font=("Consolas", 10), bg="#ffffff")
entry_headers.pack(fill='x')

# Body input
frame_body = ttk.Frame(app)
frame_body.pack(pady=10, padx=20, fill='x')
ttk.Label(frame_body, text="Body (JSON or XML)").pack(anchor="w")
entry_body = scrolledtext.ScrolledText(frame_body, height=12, wrap=tk.WORD, font=("Consolas", 10), bg="#ffffff")
entry_body.pack(fill='x')

# Send button
btn_send = ttk.Button(app, text="🚀 Send Request", command=send_request)
btn_send.pack(pady=15)

# Response output
frame_response = ttk.Frame(app)
frame_response.pack(pady=10, padx=20, fill='both', expand=True)
ttk.Label(frame_response, text="Response:").pack(anchor="w")
text_response = scrolledtext.ScrolledText(frame_response, height=15, wrap=tk.WORD, font=("Consolas", 10), bg="#f9f9f9", fg="#000000")
text_response.pack(fill='both', expand=True)

app.mainloop()</code>

Packaging as a Windows Executable

Install PyInstaller and run the following commands:

<code>pip install pyinstaller</code>
<code>pyinstaller --onefile --windowed webservice_gui.py</code>

The resulting .exe will appear in the dist/ folder and can be run without a Python environment.

Usage Examples

Example 1: Test a GET endpoint

<code>URL: https://jsonplaceholder.typicode.com/posts
Method: GET
(Body not required)</code>

Example 2: Send an XML (SOAP) request

<code>URL: http://example.com/soap-endpoint
Method: POST
Content Type: XML
Body:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://tempuri.org/ns">
  <soapenv:Header/>
  <soapenv:Body>
    <ns:GetData>
      <ns:id>123</ns:id>
    </ns:GetData>
  </soapenv:Body>
</soapenv:Envelope></code>
<code>Headers:
Content-Type: application/xml
SOAPAction: "http://tempuri.org/GetData"</code>

Conclusion

The tutorial demonstrates how to build a fully functional WebService testing tool with a graphical interface, JSON/XML support, token authentication, automatic URL cleaning, and the ability to package it as a standalone executable. Both beginners and experienced developers can extend this framework to create their own private API testing utilities.

GUIPythonJSONHTTPXMLAPI testingTkinterpyinstaller
Test Development Learning Exchange
Written by

Test Development Learning Exchange

Test Development Learning Exchange

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.