Trading 212 API Update šŸ› ļø

Why are people having to code to work out if the API works with USD or not?

Why doesn’t the documentation detail of all of this? @KrisG The documentation doesnt even mention what platforms can be used to place Market Orders? Can the documentation be updated so people aren’t wasting their time and hitting brick walls?

I clearly want the documentation to say it works with CFD, Practice if that’s the case. Right now, it doesn’t say anything contrary to that. Does it work with Invest and Stock ISA?

1 Like

@aniskywalker Sending you a DM to troubleshoot this.

They’re not supported at the moment.

2 Likes

Have you fixed the bug in your API which returns the old tickers for stocks?

For example SOFI returns IPOE ?

1 Like

This bug means the API is useless for my journaling software.

Examples include:

SOFI returns IPOE
HIMS returns OAC
GRAB returns AGC
NBIS returns YNDX

Given downloading the history returns the correct ticker - don’t understand why this is the case.

Market orders work with INVEST account - I have tested it today. This is confirmed.
Didn’t test with CFD.

Trades are executed in the primary account currency only. This is mentioned on the first page of the docs under ā€œAPI Limitationsā€.

Only Invest and ISA accounts can use the API.

Updating the docs :hourglass_not_done:

1 Like

We’ll look into this. In the meantime, you can rely on the shortName field and the isin, as those always reflect the latest info.

Update: You can place order in main currency. Previous two sell orders converted the sale amount into main currency even my buy/sell currency is set to asset currency. Thus, two buy orders worked and later it failed saying I don’t have sufficient amount. Hence, Order can be placed in the main currency (at this moment). Hopefully soon we will be able to place order in asset currency as we do not want to lose money from currency conversion in every trade.

1 Like

Only Invest and ISA accounts can use the API.

What is the endpoint for StockISA? I can see the Invest account endpoints. If you are updating the docs, please let us know when it is updated. Thank you :folded_hands:

1 Like

I use Tradesviz as a journal - they posted a JSON extract showing the old ticker - I cannot see the shortName field in that.

Examples are:
SOFI returns IPOE
HIMS returns OAC
GRAB returns AGC
NBIS returns YNDX

This is an example of what they say was returned for NBIS (wrongly as the older ticker YNDX):

{
ā€œtypeā€: ā€œMARKETā€,
ā€œidā€: 37458247377,
ā€œfillIdā€: 37458247422,
ā€œparentOrderā€: 37458247377,
ā€œtickerā€: ā€œYNDX_US_EQā€,
ā€œorderedQuantityā€: 20.0,
ā€œfilledQuantityā€: 20.0,
ā€œlimitPriceā€: null,
ā€œstopPriceā€: null,
ā€œtimeValidityā€: null,
ā€œorderedValueā€: null,
ā€œfilledValueā€: null,
ā€œexecutorā€: ā€œWEBā€,
ā€œdateModifiedā€: ā€œ2025-08-20T10:04:16.000Zā€,
ā€œdateExecutedā€: null,
ā€œdateCreatedā€: ā€œ2025-08-20T10:04:00.000Zā€,
ā€œfillResultā€: null,
ā€œfillPriceā€: 66.81,
ā€œfillCostā€: null,
ā€œtaxesā€: [
{
ā€œfillIdā€: ā€œ37458247422ā€,
ā€œnameā€: ā€œCURRENCY_CONVERSION_FEEā€,
ā€œquantityā€: -1.48,
ā€œtimeChargedā€: ā€œ2025-08-20T10:04:16.994Zā€
}
],
ā€œfillTypeā€: ā€œOTCā€,
ā€œstatusā€: ā€œFILLEDā€
},

Exactly the problem I faced when building our own journal.. there’s so many using old ticker names. I had to create an alias table. I’m interested to know where KrisG thinks the shortName field is? As that would save me a lot of manual upkeep.

shortName is in /api/v0/equity/metadata/instruments. you can get all instruments to a table a then get shortNames by ticker.

1 Like

They are identical. The token just needs to be generated for that specific account (i.e. the StockISA account).

1 Like

This is huge, thank you team! I would imagine that writing the API wouldn’t be technically too difficult, but getting it right… ā€œCorrectnessā€ remains an unsolved problem in computer science.

Meanwhile, any chance of integration with TradingView? That link outlines the process. I would volunteer to help if it were welcome (and when I get clear of my immediate backlog)!

1 Like

+1 for TradingView integration!

Thanks laqula! I’ve now imported them all.

After running quite a few tests, I think I’m finally getting there with the API setup.

The only issue I’ve got left is with the key permissions — I’ve already enabled everything, but it still seems restricted somehow.

I think I’ll wait for the full update.

Try this authorization method:

api_key = "x"
api_secret = "x"

def send_get(url: str, payload: any = None) -> requests.Response:

    response = requests.get(url, timeout=timeout_in_sec, auth=(api_key, api_secret), params=payload)

    if not response.ok:

        print(f"{response.status_code}: {response.reason}")

    return response
1 Like

I had more ideas but decided to clean up instead, thank your for confirming.


# cleanup_trading212.py
# COMPLETE Cleanup Script for All Trading212 API Tests
# Run this once to disable, secure, and clean everything

import os
import glob
import re
import shutil

def main_cleanup():
    print("šŸ›‘ COMPLETE TRADING212 SCRIPTS CLEANUP")
    print("=" * 60)
    
    # Phase 1: Disable all scripts
    disable_scripts()
    
    # Phase 2: Security check for API keys
    check_exposed_keys()
    
    # Phase 3: Cleanup temporary files
    cleanup_temp_files()
    
    # Phase 4: Final verification
    final_report()

def disable_scripts():
    """Disable all Trading212 scripts by renaming them"""
    print("\nšŸ”’ PHASE 1: DISABLING SCRIPTS")
    print("-" * 40)
    
    trading_scripts = [
        # Main API clients
        "trading212_api.py", "trading212_practice.py", "trading212_working.py",
        
        # Test and discovery scripts
        "environment_check.py", "endpoint_discovery.py", "api_discovery.py",
        "simple_test.py", "test_corrected_api.py", "test_new_key.py",
        "live_api_micro_test.py", "trading212_permission_fix.py", 
        "final_api_test.py", "validate_key.py", "test_practice_connection.py",
        
        # Alternative approaches
        "trading212_automation.py", "alternative_suggestions.py",
        "platform_migration.py", "funding_guide.py",
        
        # Cleanup scripts (will self-disable)
        "disable_trading_scripts.py", "quick_cleanup.py", "security_check.py"
    ]
    
    disabled_count = 0
    not_found = []
    
    for script in trading_scripts:
        if os.path.exists(script):
            try:
                new_name = script + ".disabled"
                os.rename(script, new_name)
                print(f"āœ… Disabled: {script} → {new_name}")
                disabled_count += 1
            except Exception as e:
                print(f"āŒ Failed to disable {script}: {e}")
        else:
            not_found.append(script)
    
    print(f"\nšŸ“Š Disabled {disabled_count} scripts")
    if not_found:
        print(f"ā„¹ļø  Not found: {len(not_found)} scripts")

def check_exposed_keys():
    """Check for any exposed API keys in Python files"""
    print("\nšŸ” PHASE 2: SECURITY CHECK FOR API KEYS")
    print("-" * 40)
    
    key_patterns = [
        r'API_KEY\s*=\s*["\'][^"\']{10,}["\']',
        r'api_key\s*=\s*["\'][^"\']{10,}["\']',
        r'LIVE_API_KEY\s*=\s*["\'][^"\']{10,}["\']',
        r'NEW_API_KEY\s*=\s*["\'][^"\']{10,}["\']',
        r'T212-API-KEY\s+[^\s]{20,}',
        r'Bearer\s+[^\s]{20,}',
        r'["\'][a-zA-Z0-9]{30,50}["\']'  # Generic long strings that might be keys
    ]
    
    exposed_files = []
    
    for file in glob.glob("*.py") + glob.glob("*.py.disabled"):
        try:
            with open(file, 'r', encoding='utf-8', errors='ignore') as f:
                content = f.read()
                lines = content.split('\n')
            
            for i, line in enumerate(lines, 1):
                for pattern in key_patterns:
                    if re.search(pattern, line, re.IGNORECASE):
                        # Check if it's a placeholder
                        if not any(placeholder in line for placeholder in ["YOUR_", "PLACEHOLDER", "EXAMPLE"]):
                            exposed_files.append((file, i, line.strip()))
                            break
        
        except Exception as e:
            print(f"āš ļø  Could not read {file}: {e}")
    
    if exposed_files:
        print("🚨 POTENTIAL API KEYS FOUND:")
        for file, line_num, line_content in exposed_files:
            print(f"   šŸ“ {file}: Line {line_num}")
            print(f"      {line_content[:80]}...")
        
        print("\nšŸ”’ Please manually remove API keys from these files!")
    else:
        print("āœ… No exposed API keys found")

def cleanup_temp_files():
    """Remove all temporary and generated files"""
    print("\n🧹 PHASE 3: CLEANING TEMPORARY FILES")
    print("-" * 40)
    
    # Files and folders to remove
    cleanup_targets = [
        "__pycache__",
        "*.pyc",
        "*.log",
        "test_*.json",
        "debug_*.txt",
        "*.tmp",
        "temp_*"
    ]
    
    removed_count = 0
    
    for pattern in cleanup_targets:
        for item in glob.glob(pattern):
            try:
                if os.path.isdir(item):
                    shutil.rmtree(item)
                    print(f"šŸ—‘ļø  Removed folder: {item}")
                else:
                    os.remove(item)
                    print(f"šŸ—‘ļø  Removed file: {item}")
                removed_count += 1
            except Exception as e:
                print(f"āš ļø  Could not remove {item}: {e}")
    
    print(f"šŸ“Š Removed {removed_count} temporary items")

def final_report():
    """Generate final cleanup report"""
    print("\nšŸ“‹ PHASE 4: FINAL CLEANUP REPORT")
    print("-" * 40)
    
    # Count remaining files
    python_files = glob.glob("*.py")
    disabled_files = glob.glob("*.py.disabled")
    
    print(f"šŸ“ Active Python files: {len(python_files)}")
    print(f"šŸ“ Disabled Python files: {len(disabled_files)}")
    
    if python_files:
        print("\nšŸ” Remaining active files:")
        for file in python_files:
            print(f"   - {file}")
    
    print("\nšŸŽÆ CLEANUP ACTIONS COMPLETED:")
    print("āœ… All Trading212 scripts disabled")
    print("āœ… API key security check performed") 
    print("āœ… Temporary files cleaned up")
    print("āœ… Desktop environment secured")
    
    print("\nšŸ’” RECOMMENDATIONS:")
    print("1. Manually delete .disabled files if you want complete removal")
    print("2. Empty Recycle Bin for permanent deletion")
    print("3. Consider using virtual environments for future projects")
    print("4. Store API keys in environment variables, not code")

def safe_self_cleanup():
    """Safely handle self-cleanup of this script"""
    print("\n" + "=" * 60)
    response = input("🧹 Delete this cleanup script too? (y/n): ").lower().strip()
    
    if response in ['y', 'yes']:
        try:
            current_script = os.path.basename(__file__)
            if os.path.exists(current_script):
                os.remove(current_script)
                print(f"āœ… Deleted: {current_script}")
            print("šŸŽ‰ CLEANUP COMPLETE! All scripts removed.")
        except Exception as e:
            print(f"āŒ Could not delete this script: {e}")
            print("šŸ’” Please manually delete cleanup_trading212.py")
    else:
        print("šŸ’” This cleanup script remains for future use.")
        print("   Run it again if you need to clean up more files.")

if __name__ == "__main__":
    try:
        main_cleanup()
        safe_self_cleanup()
    except Exception as e:
        print(f"šŸ’„ Cleanup error: {e}")
        print("šŸ’” Please run the script again or manually delete files.")
    
    print("\n" + "=" * 60)
    print("šŸ CLEANUP PROCESS FINISHED")
    print("=" * 60)